diff --git a/libraries/config/board/qemu/virt/board.mk b/libraries/config/board/qemu/virt/board.mk new file mode 100644 index 000000000..752aad417 --- /dev/null +++ b/libraries/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/libraries/config/common.mk b/libraries/config/common.mk index 3fd7c1cad..460f7d9ea 100644 --- a/libraries/config/common.mk +++ b/libraries/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/libraries/libmesosphere/Makefile b/libraries/libmesosphere/Makefile index c60cc8650..6a996f156 100644 --- a/libraries/libmesosphere/Makefile +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp index c06c4e30d..f8c39552b 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp +++ b/libraries/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 @@ -50,6 +52,10 @@ namespace ams::kern::arch::arm64::cpu { __asm__ __volatile__("dmb sy" ::: "memory"); } + ALWAYS_INLINE void DataMemoryBarrierInnerShareable() { + __asm__ __volatile__("dmb ish" ::: "memory"); + } + ALWAYS_INLINE void InstructionMemoryBarrier() { __asm__ __volatile__("isb" ::: "memory"); } diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp index cbe6e1ea3..6503b4e1e 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_secure_monitor_base.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_secure_monitor_base.hpp new file mode 100644 index 000000000..76ec13937 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_secure_monitor_base.hpp @@ -0,0 +1,95 @@ +/* + * 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 +#include +#include + +namespace ams::kern::arch::arm64::smc { + + template + void SecureMonitorCall(u64 *buf) { + /* Load arguments into registers. */ + register u64 x0 asm("x0") = buf[0]; + register u64 x1 asm("x1") = buf[1]; + register u64 x2 asm("x2") = buf[2]; + register u64 x3 asm("x3") = buf[3]; + register u64 x4 asm("x4") = buf[4]; + register u64 x5 asm("x5") = buf[5]; + register u64 x6 asm("x6") = buf[6]; + register u64 x7 asm("x7") = buf[7]; + + /* Perform the call. */ + if constexpr (DisableInterrupt) { + KScopedInterruptDisable di; + + /* Backup the current thread pointer. */ + const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue(); + + __asm__ __volatile__("smc %c[smc_id]" + : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7) + : [smc_id]"i"(SmcId) + : "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); + } else { + /* Backup the current thread pointer. */ + const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue(); + + __asm__ __volatile__("smc %c[smc_id]" + : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7) + : [smc_id]"i"(SmcId) + : "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. */ + buf[0] = x0; + buf[1] = x1; + buf[2] = x2; + buf[3] = x3; + buf[4] = x4; + buf[5] = x5; + buf[6] = x6; + buf[7] = x7; + } + + enum PsciFunction { + PsciFunction_CpuSuspend = 0xC4000001, + PsciFunction_CpuOff = 0x84000002, + PsciFunction_CpuOn = 0xC4000003, + }; + + template + u64 PsciCall(PsciFunction function, u64 x1 = 0, u64 x2 = 0, u64 x3 = 0, u64 x4 = 0, u64 x5 = 0, u64 x6 = 0, u64 x7 = 0) { + ams::svc::lp64::SecureMonitorArguments args = { { function, x1, x2, x3, x4, x5, x6, x7 } }; + + SecureMonitorCall(args.r); + + return args.r[0]; + } + + template + u64 CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) { + return PsciCall(PsciFunction_CpuOn, core_id, entrypoint, arg); + } + +} diff --git a/libraries/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp b/libraries/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp index cc9404a41..0fb09b4ef 100644 --- a/libraries/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp index 003f74570..b12e8d02e 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp @@ -15,9 +15,12 @@ */ #pragma once #include +#include namespace ams::kern { + constexpr inline KPhysicalAddress MainMemoryAddress = 0x80000000; + constexpr inline size_t MainMemorySize = 4_GB; constexpr inline size_t MainMemorySizeMax = 8_GB; diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index b7c2f1697..2349e3a99 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -15,23 +15,17 @@ */ #pragma once #include - -namespace ams::kern { - - struct InitialProcessBinaryLayout; - -} +#include namespace ams::kern::board::nintendo::nx { - class KSystemControl { + class KSystemControl : public KSystemControlBase { public: - class Init { + class Init : public KSystemControlBase::Init { public: /* Initialization. */ + static size_t GetRealMemorySize(); 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(); @@ -40,7 +34,7 @@ namespace ams::kern::board::nintendo::nx { static u8 GetDebugLogUartPort(); /* Randomness. */ - static void GenerateRandomBytes(void *dst, size_t size); + static void GenerateRandom(u64 *dst, size_t count); static u64 GenerateRandomRange(u64 min, u64 max); }; public: @@ -50,7 +44,7 @@ namespace ams::kern::board::nintendo::nx { static NOINLINE u32 GetCreateProcessMemoryPool(); /* Randomness. */ - static void GenerateRandomBytes(void *dst, size_t size); + static void GenerateRandom(u64 *dst, size_t count); static u64 GenerateRandomRange(u64 min, u64 max); static u64 GenerateRandomU64(); @@ -58,23 +52,12 @@ namespace ams::kern::board::nintendo::nx { 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); + static void CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args); /* Secure Memory. */ static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool); diff --git a/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp b/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp new file mode 100644 index 000000000..ec3335466 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp new file mode 100644 index 000000000..b18ec45c7 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::kern { + + constexpr inline KPhysicalAddress MainMemoryAddress = 0x40000000; + + constexpr inline size_t MainMemorySize = 4_GB; + constexpr inline size_t MainMemorySizeMax = 8_GB; + +} diff --git a/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc b/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc new file mode 100644 index 000000000..3d64f0407 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp new file mode 100644 index 000000000..78a94fa18 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp @@ -0,0 +1,28 @@ +/* + * 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 +#include + +namespace ams::kern::board::qemu::virt { + + class KSystemControl : public KSystemControlBase { + public: + /* User access. */ + static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); + }; + +} \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp index 8216bab0c..cb0de2781 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp @@ -38,4 +38,13 @@ /* at the cost of storing class tokens inside the class object. */ /* However, as of (10/16/2021) KAutoObject has an unused class member */ /* of the right side, and so this does not actually cost any space. */ -#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST \ No newline at end of file +#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST + +/* NOTE: This uses currently-reserved bits inside the MapRange capability */ +/* in order to support large physical addresses (40-bit instead of 36). */ +/* This is toggleable in order to disable it if N ever uses those bits. */ +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) +//#define MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES +#else +#define MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES +#endif \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp b/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp index 8253b1a24..51b83596e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index fd69590b8..3c31124d8 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -82,7 +82,11 @@ namespace ams::kern { DEFINE_FIELD(Index, Mask, 3); }; + #if defined(MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES) + static constexpr u64 PhysicalMapAllowedMask = (1ul << 40) - 1; + #else static constexpr u64 PhysicalMapAllowedMask = (1ul << 36) - 1; + #endif struct MapRange { using IdBits = Field<0, CapabilityId + 1>; @@ -94,9 +98,15 @@ namespace ams::kern { struct MapRangeSize { using IdBits = Field<0, CapabilityId + 1>; - DEFINE_FIELD(Pages, IdBits, 20); + DEFINE_FIELD(Pages, IdBits, 20); + + #if defined(MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES) + DEFINE_FIELD(AddressHigh, Pages, 4); + DEFINE_FIELD(Normal, AddressHigh, 1, bool); + #else DEFINE_FIELD(Reserved, Pages, 4); DEFINE_FIELD(Normal, Reserved, 1, bool); + #endif }; struct MapIoPage { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index 66ee75ab6..34d3d2e6a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/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 @@ -210,13 +212,17 @@ namespace ams::kern { static NOINLINE auto GetKernelPageTableHeapRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelPtHeap); } static NOINLINE auto GetKernelInitPageTableRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelInitPt); } - static NOINLINE auto GetKernelPoolManagementRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolManagement); } static NOINLINE auto GetKernelPoolPartitionRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolPartition); } + static NOINLINE auto GetKernelPoolManagementRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolManagement); } static NOINLINE auto GetKernelSystemPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemPool); } static NOINLINE auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemNonSecurePool); } static NOINLINE auto GetKernelAppletPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramAppletPool); } static NOINLINE auto GetKernelApplicationPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramApplicationPool); } + static NOINLINE bool HasKernelSystemNonSecurePoolRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramSystemNonSecurePool) != nullptr; } + static NOINLINE bool HasKernelAppletPoolRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramAppletPool) != nullptr; } + static NOINLINE bool HasKernelApplicationPoolRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramApplicationPool) != nullptr; } + static NOINLINE auto GetKernelTraceBufferRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelTraceBuffer); } }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp index 7f9cea68b..97934c534 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp @@ -48,39 +48,20 @@ namespace ams::kern { } size_t SelectRandomBit(u64 bitmap) { - u64 selected = 0; + u64 selected = 0; - u64 cur_num_bits = BITSIZEOF(bitmap) / 2; - u64 cur_mask = (1ull << cur_num_bits) - 1; + for (size_t cur_num_bits = BITSIZEOF(bitmap) / 2; cur_num_bits != 0; cur_num_bits /= 2) { + const u64 high = (bitmap >> cur_num_bits); + const u64 low = (bitmap & (~(UINT64_C(0xFFFFFFFFFFFFFFFF) << cur_num_bits))); - while (cur_num_bits) { - const u64 low = (bitmap >> 0) & cur_mask; - const u64 high = (bitmap >> cur_num_bits) & cur_mask; - - bool choose_low; - if (high == 0) { - /* If only low val is set, choose low. */ - choose_low = true; - } else if (low == 0) { - /* If only high val is set, choose high. */ - choose_low = false; - } else { - /* If both are set, choose random. */ - choose_low = this->GenerateRandomBit(); - } - - /* If we chose low, proceed with low. */ - if (choose_low) { - bitmap = low; - selected += 0; - } else { + /* Choose high if we have high and (don't have low or select high randomly). */ + if (high && (low == 0 || this->GenerateRandomBit())) { bitmap = high; selected += cur_num_bits; + } else { + bitmap = low; + selected += 0; } - - /* Proceed. */ - cur_num_bits /= 2; - cur_mask >>= cur_num_bits; } return selected; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp index 10896f4fc..e12ac1184 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler_lock.hpp @@ -74,6 +74,9 @@ namespace ams::kern { /* Release an instance of the lock. */ if ((--m_lock_count) == 0) { + /* Perform a memory barrier here. */ + cpu::DataMemoryBarrierInnerShareable(); + /* We're no longer going to hold the lock. Take note of what cores need scheduling. */ const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_system_control_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_system_control_base.hpp new file mode 100644 index 000000000..2abfc948a --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_system_control_base.hpp @@ -0,0 +1,110 @@ +/* + * 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 +#include + +namespace ams::kern { + + struct InitialProcessBinaryLayout; + +} + +namespace ams::kern { + + class KSystemControlBase { + protected: + /* Nintendo uses std::mt19937_t for randomness. */ + /* To save space (and because mt19337_t isn't secure anyway), */ + /* We will use TinyMT. */ + static constinit inline bool s_initialized_random_generator; + static constinit inline util::TinyMT s_random_generator{util::ConstantInitialize}; + static constinit inline KSpinLock s_random_lock; + public: + class Init { + public: + /* Initialization. */ + static size_t GetRealMemorySize(); + static size_t GetIntendedMemorySize(); + static KPhysicalAddress GetKernelPhysicalBaseAddress(KPhysicalAddress 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 GenerateRandom(u64 *dst, size_t count); + static u64 GenerateRandomRange(u64 min, u64 max); + }; + public: + /* Initialization. */ + static NOINLINE void InitializePhase1(bool skip_target_system = false); + static NOINLINE void InitializePhase2(); + static NOINLINE u32 GetCreateProcessMemoryPool(); + + /* Randomness. */ + static void GenerateRandom(u64 *dst, size_t count); + static u64 GenerateRandomRange(u64 min, u64 max); + static u64 GenerateRandomU64(); + + /* Register access Access. */ + static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); + static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); + + static u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address); + static void WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value); + + /* Power management. */ + static void SleepSystem(); + static NORETURN void StopSystem(void *arg = nullptr); + + /* User access. */ + #if defined(ATMOSPHERE_ARCH_ARM64) + static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); + #endif + + /* 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); + protected: + template + static 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); + } + } + } + + /* User access. */ + #if defined(ATMOSPHERE_ARCH_ARM64) + static void CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args); + #endif + }; + +} \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_target_system.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_target_system.hpp index bcb53ec20..87b72c0fa 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_target_system.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_target_system.hpp @@ -21,6 +21,7 @@ namespace ams::kern { class KTargetSystem { private: + friend class KSystemControlBase; friend class KSystemControl; private: static inline constinit bool s_is_debug_mode; diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_cpu.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_cpu.hpp index 8cf08533b..799a2fb3f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_select_cpu.hpp +++ b/libraries/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/libraries/libmesosphere/include/mesosphere/kern_select_system_control.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_system_control.hpp index cb1bb4d9a..cc0edc55c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_select_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_select_system_control.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include #ifdef ATMOSPHERE_BOARD_NINTENDO_NX #include @@ -23,6 +24,28 @@ 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 + +namespace ams::kern { + + ALWAYS_INLINE u32 KSystemControlBase::ReadRegisterPrivileged(ams::svc::PhysicalAddress address) { + u32 v; + KSystemControl::ReadWriteRegisterPrivileged(std::addressof(v), address, 0x00000000u, 0); + return v; + } + + ALWAYS_INLINE void KSystemControlBase::WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value) { + u32 v; + KSystemControl::ReadWriteRegisterPrivileged(std::addressof(v), address, 0xFFFFFFFFu, value); + } + +} diff --git a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp index 6225f28a8..bd07b173e 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp @@ -76,7 +76,7 @@ namespace ams::kern::arch::arm64::cpu { } else { m_counter = cpu::GetPerformanceCounter(m_which); } - DataMemoryBarrier(); + DataMemoryBarrierInnerShareable(); m_done = true; return nullptr; } @@ -384,11 +384,13 @@ namespace ams::kern::arch::arm64::cpu { /* Store cache from L1 up to (level of coherence - 1). */ for (int level = 0; level < levels_of_coherency - 1; ++level) { PerformCacheOperationBySetWayImpl(level, StoreDataCacheLineBySetWayImpl); + DataSynchronizationBarrier(); } /* Flush cache from (level of coherence - 1) down to L0. */ for (int level = levels_of_coherency; level > 0; --level) { PerformCacheOperationBySetWayImpl(level - 1, FlushDataCacheLineBySetWayImpl); + DataSynchronizationBarrier(); } } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index 1a244b362..6927c714c 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -1130,7 +1130,7 @@ namespace ams::kern::board::nintendo::nx { size_t cur_size; { /* Get the current contiguous range. */ - KPageTableBase::MemoryRange contig_range = {}; + KPageTableBase::MemoryRange contig_range = { .address = Null, .size = 0 }; R_TRY(page_table->OpenMemoryRangeForMapDeviceAddressSpace(std::addressof(contig_range), process_address + mapped_size, size - mapped_size, ConvertToKMemoryPermission(device_perm), is_aligned)); /* Ensure we close the range when we're done. */ @@ -1288,7 +1288,7 @@ namespace ams::kern::board::nintendo::nx { MESOSPHERE_ASSERT(((device_address + size - 1) & ~DeviceVirtualAddressMask) == 0); /* We need to traverse the ranges that make up our mapping, to make sure they're all good. Start by getting a contiguous range. */ - KPageTableBase::MemoryRange contig_range = {}; + KPageTableBase::MemoryRange contig_range = { .address = Null, .size = 0 }; if (R_FAILED(page_table->OpenMemoryRangeForUnmapDeviceAddressSpace(std::addressof(contig_range), process_address, size))) { return false; } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp index 2a08929cb..713a8e0c6 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp @@ -73,7 +73,7 @@ namespace ams::kern::board::nintendo::nx { void PowerOnCpu(int core_id, KPhysicalAddress entry_phys_addr, u64 context_id) { /* Request the secure monitor power on the core. */ - smc::CpuOn(cpu::MultiprocessorAffinityRegisterAccessor().GetCpuOnArgument() | core_id, GetInteger(entry_phys_addr), context_id); + ::ams::kern::arch::arm64::smc::CpuOn(cpu::MultiprocessorAffinityRegisterAccessor().GetCpuOnArgument() | core_id, GetInteger(entry_phys_addr), context_id); } void WaitOtherCpuPowerOff() { diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 3f91ecfb7..ed1fc14b9 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -21,8 +21,7 @@ namespace ams::kern::board::nintendo::nx { namespace { - constexpr uintptr_t DramPhysicalAddress = 0x80000000; - constexpr size_t SecureAlignment = 128_KB; + constexpr size_t SecureAlignment = 128_KB; /* Global variables for panic. */ constinit bool g_call_smc_on_panic; @@ -38,22 +37,6 @@ namespace ams::kern::board::nintendo::nx { constinit KPhysicalAddress g_secure_region_phys_addr = Null; constinit size_t g_secure_region_size = 0; - /* Global variables for randomness. */ - /* Nintendo uses std::mt19937_t for randomness. */ - /* To save space (and because mt19337_t isn't secure anyway), */ - /* We will use TinyMT. */ - constinit bool g_initialized_random_generator; - constinit util::TinyMT g_random_generator{util::ConstantInitialize}; - constinit KSpinLock g_random_lock; - - ALWAYS_INLINE size_t GetRealMemorySizeForInit() { - /* TODO: Move this into a header for the MC in general. */ - constexpr u32 MemoryControllerConfigurationRegister = 0x70019050; - u32 config_value; - MESOSPHERE_INIT_ABORT_UNLESS(smc::init::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0)); - return static_cast(config_value & 0x3FFF) << 20; - } - ALWAYS_INLINE util::BitPack32 GetKernelConfigurationForInit() { u64 value = 0; smc::init::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration); @@ -86,7 +69,7 @@ namespace ams::kern::board::nintendo::nx { ALWAYS_INLINE u64 GenerateRandomU64ForInit() { u64 value; - smc::init::GenerateRandomBytes(&value, sizeof(value)); + smc::init::GenerateRandomBytes(std::addressof(value), sizeof(value)); return value; } @@ -96,27 +79,6 @@ namespace ams::kern::board::nintendo::nx { return value; } - 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); - } - } - } - ALWAYS_INLINE u64 GetConfigU64(smc::ConfigItem which) { u64 value; smc::GetConfig(&value, 1, which); @@ -324,6 +286,14 @@ namespace ams::kern::board::nintendo::nx { } /* Initialization. */ + size_t KSystemControl::Init::GetRealMemorySize() { + /* TODO: Move this into a header for the MC in general. */ + constexpr u32 MemoryControllerConfigurationRegister = 0x70019050; + u32 config_value; + MESOSPHERE_INIT_ABORT_UNLESS(smc::init::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0)); + return static_cast(config_value & 0x3FFF) << 20; + } + size_t KSystemControl::Init::GetIntendedMemorySize() { switch (GetKernelConfigurationForInit().Get()) { case smc::MemorySize_4GB: @@ -336,23 +306,6 @@ namespace ams::kern::board::nintendo::nx { } } - 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 GetKernelConfigurationForInit().Get(); } @@ -424,17 +377,17 @@ namespace ams::kern::board::nintendo::nx { } void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) { - smc::init::CpuOn(core_id, entrypoint, arg); + MESOSPHERE_INIT_ABORT_UNLESS((::ams::kern::arch::arm64::smc::CpuOn(core_id, entrypoint, arg)) == 0); } /* Randomness for Initialization. */ - void KSystemControl::Init::GenerateRandomBytes(void *dst, size_t size) { - MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38); - smc::init::GenerateRandomBytes(dst, size); + void KSystemControl::Init::GenerateRandom(u64 *dst, size_t count) { + MESOSPHERE_INIT_ABORT_UNLESS(count <= 7); + smc::init::GenerateRandomBytes(dst, count * sizeof(u64)); } u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) { - return GenerateUniformRange(min, max, GenerateRandomU64ForInit); + return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64ForInit); } /* System Initialization. */ @@ -443,8 +396,8 @@ namespace ams::kern::board::nintendo::nx { { u64 seed; smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed)); - g_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); - g_initialized_random_generator = true; + s_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); + s_initialized_random_generator = true; } /* Set IsDebugMode. */ @@ -483,25 +436,8 @@ namespace ams::kern::board::nintendo::nx { smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize()); } - /* 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)); - } + /* Initialize the system resource limit (and potentially other things). */ + KSystemControlBase::InitializePhase1(true); } void KSystemControl::InitializePhase2() { @@ -520,11 +456,8 @@ namespace ams::kern::board::nintendo::nx { 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()); - } + /* Initialize KTrace (and potentially other init). */ + KSystemControlBase::InitializePhase2(); } u32 KSystemControl::GetCreateProcessMemoryPool() { @@ -546,29 +479,29 @@ namespace ams::kern::board::nintendo::nx { } /* Randomness. */ - void KSystemControl::GenerateRandomBytes(void *dst, size_t size) { - MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38); - smc::GenerateRandomBytes(dst, size); + void KSystemControl::GenerateRandom(u64 *dst, size_t count) { + MESOSPHERE_INIT_ABORT_UNLESS(count <= 7); + smc::GenerateRandomBytes(dst, count * sizeof(u64)); } u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) { KScopedInterruptDisable intr_disable; - KScopedSpinLock lk(g_random_lock); + KScopedSpinLock lk(s_random_lock); - if (AMS_LIKELY(g_initialized_random_generator)) { - return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator); + if (AMS_LIKELY(s_initialized_random_generator)) { + return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); }); } else { - return GenerateUniformRange(min, max, GenerateRandomU64FromSmc); + return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc); } } u64 KSystemControl::GenerateRandomU64() { KScopedInterruptDisable intr_disable; - KScopedSpinLock lk(g_random_lock); + KScopedSpinLock lk(s_random_lock); - if (AMS_LIKELY(g_initialized_random_generator)) { - return GenerateRandomU64FromGenerator(); + if (AMS_LIKELY(s_initialized_random_generator)) { + return s_random_generator.GenerateRandomU64(); } else { return GenerateRandomU64FromSmc(); } @@ -672,52 +605,18 @@ namespace ams::kern::board::nintendo::nx { } /* 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; - } - } - } - + void KSystemControl::CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args) { /* 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(); - } + return smc::CallSecureMonitorFromUser(args); } /* Secure Memory. */ size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) { if (pool == KMemoryManager::Pool_Applet) { return 0; + } else { + return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool); } - return size; } Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) { diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 112524fae..6b1e3923f 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp @@ -20,18 +20,27 @@ namespace ams::kern::board::nintendo::nx::smc { namespace { - struct SecureMonitorArguments { - u64 x[8]; - }; - 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 { - FunctionId_CpuSuspend = 0xC4000001, - FunctionId_CpuOff = 0x84000002, - FunctionId_CpuOn = 0xC4000003, FunctionId_GetConfig = 0xC3000004, FunctionId_GenerateRandomBytes = 0xC3000005, FunctionId_Panic = 0xC3000006, @@ -42,171 +51,60 @@ namespace ams::kern::board::nintendo::nx::smc { FunctionId_SetConfig = 0xC3000409, }; - 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 #1" - : "+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 CallUserSecureMonitorFunction(ams::svc::lp64::SecureMonitorArguments *args) { - /* Load arguments into registers. */ - register u64 x0 asm("x0") = args->r[0]; - register u64 x1 asm("x1") = args->r[1]; - register u64 x2 asm("x2") = args->r[2]; - register u64 x3 asm("x3") = args->r[3]; - register u64 x4 asm("x4") = args->r[4]; - register u64 x5 asm("x5") = args->r[5]; - register u64 x6 asm("x6") = args->r[6]; - register u64 x7 asm("x7") = args->r[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->r[0] = x0; - args->r[1] = x1; - args->r[2] = x2; - args->r[3] = x3; - args->r[4] = x4; - args->r[5] = x5; - args->r[6] = x6; - args->r[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 #1" - : "+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; + constinit 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 GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { - SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; - CallPrivilegedSecureMonitorFunctionForInit(args); - MESOSPHERE_INIT_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GetConfig, static_cast(config_item) } }; + + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + MESOSPHERE_INIT_ABORT_UNLESS((static_cast(args.r[0]) == SmcResult::Success)); + for (size_t i = 0; i < num_qwords && i < 7; i++) { - out[i] = args.x[1 + i]; + out[i] = args.r[1 + i]; } } void GenerateRandomBytes(void *dst, size_t size) { /* Call SmcGenerateRandomBytes() */ - SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size }; - MESOSPHERE_INIT_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0])); + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GenerateRandomBytes, size } }; + MESOSPHERE_INIT_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.r[0])); - CallPrivilegedSecureMonitorFunctionForInit(args); - MESOSPHERE_INIT_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + MESOSPHERE_INIT_ABORT_UNLESS((static_cast(args.r[0]) == SmcResult::Success)); /* Copy output. */ - std::memcpy(dst, std::addressof(args.x[1]), size); + std::memcpy(dst, std::addressof(args.r[1]), size); } bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) { - SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value }; - CallPrivilegedSecureMonitorFunctionForInit(args); - *out = args.x[1]; - return static_cast(args.x[0]) == SmcResult::Success; + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ReadWriteRegister, address, mask, value } }; + + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + MESOSPHERE_INIT_ABORT_UNLESS((static_cast(args.r[0]) == SmcResult::Success)); + + *out = args.r[1]; + + return static_cast(args.r[0]) == SmcResult::Success; } } bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { - SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; - CallPrivilegedSecureMonitorFunction(args); - if (static_cast(args.x[0]) != SmcResult::Success) { + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GetConfig, static_cast(config_item) } }; + + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + if (AMS_UNLIKELY(static_cast(args.r[0]) != SmcResult::Success)) { return false; } for (size_t i = 0; i < num_qwords && i < 7; i++) { - out[i] = args.x[1 + i]; + out[i] = args.r[1 + i]; } return true; @@ -217,55 +115,58 @@ namespace ams::kern::board::nintendo::nx::smc { } bool SetConfig(ConfigItem config_item, u64 value) { - SecureMonitorArguments args = { FunctionId_SetConfig, static_cast(config_item), 0, value }; - CallPrivilegedSecureMonitorFunction(args); - return static_cast(args.x[0]) == SmcResult::Success; + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_SetConfig, static_cast(config_item), 0, value } }; + + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + + return static_cast(args.r[0]) == SmcResult::Success; } bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) { - SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value }; - CallPrivilegedSecureMonitorFunction(args); - *out = static_cast(args.x[1]); - return static_cast(args.x[0]) == SmcResult::Success; + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ReadWriteRegister, address, mask, value } }; + + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + + *out = static_cast(args.r[1]); + return static_cast(args.r[0]) == SmcResult::Success; } void ConfigureCarveout(size_t which, uintptr_t address, size_t size) { - SecureMonitorArguments args = { FunctionId_ConfigureCarveout, static_cast(which), static_cast(address), static_cast(size) }; - CallPrivilegedSecureMonitorFunction(args); - MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); - } + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_ConfigureCarveout, static_cast(which), static_cast(address), static_cast(size) } }; - 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)); + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + + MESOSPHERE_ABORT_UNLESS((static_cast(args.r[0]) == SmcResult::Success)); } void GenerateRandomBytes(void *dst, size_t size) { /* Setup for call. */ - SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size }; - MESOSPHERE_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0])); + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_GenerateRandomBytes, size } }; + MESOSPHERE_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.r[0])); /* Make call. */ { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(g_generate_random_lock); - CallPrivilegedSecureMonitorFunction(args); + + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); } - MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + MESOSPHERE_ABORT_UNLESS((static_cast(args.r[0]) == SmcResult::Success)); /* Copy output. */ - std::memcpy(dst, std::addressof(args.x[1]), size); + std::memcpy(dst, std::addressof(args.r[1]), size); } void NORETURN Panic(u32 color) { - SecureMonitorArguments args = { FunctionId_Panic, color }; - CallPrivilegedSecureMonitorFunction(args); + ams::svc::lp64::SecureMonitorArguments args = { { FunctionId_Panic, color } }; + + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args.r); + AMS_INFINITE_LOOP(); } void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) { - CallUserSecureMonitorFunction(args); + ::ams::kern::arch::arm64::smc::SecureMonitorCall(args->r); } } \ No newline at end of file diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index cf1d0cdac..b172fa540 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp @@ -15,10 +15,16 @@ */ #pragma once #include +#include namespace ams::kern::board::nintendo::nx::smc { /* Types. */ + enum SmcId { + SmcId_User = 0, + SmcId_Supervisor = 1, + }; + enum MemorySize { MemorySize_4GB = 0, MemorySize_6GB = 1, @@ -105,15 +111,12 @@ namespace ams::kern::board::nintendo::nx::smc { bool SetConfig(ConfigItem config_item, u64 value); - void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg); - void NORETURN Panic(u32 color); void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); namespace init { - void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg); void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); void GenerateRandomBytes(void *dst, size_t size); bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value); diff --git a/libraries/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp new file mode 100644 index 000000000..59972ef65 --- /dev/null +++ b/libraries/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp @@ -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 . + */ +#include +#include "kern_secure_monitor.hpp" + +namespace ams::kern::board::qemu::virt { + + /* User access. */ + void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) { + /* Invoke the secure monitor. */ + return smc::CallSecureMonitorFromUser(args); + } + +} \ No newline at end of file diff --git a/libraries/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp new file mode 100644 index 000000000..a97fd53b7 --- /dev/null +++ b/libraries/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp @@ -0,0 +1,71 @@ +/* + * 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 { + + 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, + }; + + } + + 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/libraries/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp new file mode 100644 index 000000000..8aeaec112 --- /dev/null +++ b/libraries/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp @@ -0,0 +1,68 @@ +/* + * 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 CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); + +} \ No newline at end of file diff --git a/libraries/libmesosphere/source/kern_debug_log.cpp b/libraries/libmesosphere/source/kern_debug_log.cpp index 6ff3df949..5f296b65b 100644 --- a/libraries/libmesosphere/source/kern_debug_log.cpp +++ b/libraries/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/libraries/libmesosphere/source/kern_debug_log_impl.arch.arm64.s b/libraries/libmesosphere/source/kern_debug_log_impl.arch.arm64.s new file mode 100644 index 000000000..e7d4423e3 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp b/libraries/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp new file mode 100644 index 000000000..a71b904d4 --- /dev/null +++ b/libraries/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/libraries/libmesosphere/source/kern_debug_log_impl.hpp b/libraries/libmesosphere/source/kern_debug_log_impl.hpp index fb065c532..a34591db4 100644 --- a/libraries/libmesosphere/source/kern_debug_log_impl.hpp +++ b/libraries/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/libraries/libmesosphere/source/kern_k_capabilities.cpp b/libraries/libmesosphere/source/kern_k_capabilities.cpp index d2000e632..1aa8f4999 100644 --- a/libraries/libmesosphere/source/kern_k_capabilities.cpp +++ b/libraries/libmesosphere/source/kern_k_capabilities.cpp @@ -117,11 +117,15 @@ namespace ams::kern { } Result KCapabilities::MapRange(const util::BitPack32 cap, const util::BitPack32 size_cap, KProcessPageTable *page_table) { + /* Get/validate address/size */ + #if defined(MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES) + const u64 phys_addr = static_cast(cap.Get() | (size_cap.Get() << MapRange::Address::Count)) * PageSize; + #else + const u64 phys_addr = static_cast(cap.Get()) * PageSize; + /* Validate reserved bits are unused. */ R_UNLESS(size_cap.Get() == 0, svc::ResultOutOfRange()); - - /* Get/validate address/size */ - const u64 phys_addr = cap.Get() * PageSize; + #endif const size_t num_pages = size_cap.Get(); const size_t size = num_pages * PageSize; R_UNLESS(phys_addr == GetInteger(KPhysicalAddress(phys_addr)), svc::ResultInvalidAddress()); diff --git a/libraries/libmesosphere/source/kern_k_condition_variable.cpp b/libraries/libmesosphere/source/kern_k_condition_variable.cpp index 764ca0bcd..97e4402b4 100644 --- a/libraries/libmesosphere/source/kern_k_condition_variable.cpp +++ b/libraries/libmesosphere/source/kern_k_condition_variable.cpp @@ -250,7 +250,7 @@ namespace ams::kern { { const u32 has_waiter_flag = 1; WriteToUser(key, std::addressof(has_waiter_flag)); - cpu::DataMemoryBarrier(); + cpu::DataMemoryBarrierInnerShareable(); } /* Write the value to userspace. */ diff --git a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp index e0cb860d1..5e8357711 100644 --- a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp +++ b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp @@ -180,9 +180,11 @@ namespace ams::kern { } /* Flush caches. */ - /* NOTE: official kernel does an entire cache flush by set/way here, which is incorrect as other cores are online. */ - /* We will simply flush by virtual address, since that's what ARM says is correct to do. */ - MESOSPHERE_R_ABORT_UNLESS(cpu::FlushDataCache(GetVoidPointer(address), params.code_num_pages * PageSize)); + /* NOTE: This seems incorrect according to arm spec, which says not to flush via set/way after boot. */ + /* However, Nintendo flushes the entire cache here and not doing so has caused reports of abort with ESR_EL1 */ + /* as 0x02000000 (unknown abort) to occur. */ + MESOSPHERE_UNUSED(params); + cpu::FlushEntireDataCache(); cpu::InvalidateEntireInstructionCache(); return ResultSuccess(); diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp index 8f20de0e7..a0b4d303f 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp @@ -19,7 +19,6 @@ namespace ams::kern { namespace { - constexpr uintptr_t DramPhysicalAddress = 0x80000000; constexpr size_t ReservedEarlyDramSize = 0x60000; constexpr size_t CarveoutAlignment = 0x20000; @@ -100,7 +99,7 @@ namespace ams::kern { void SetupDramPhysicalMemoryRegions() { const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize(); - const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress); + const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(ams::kern::MainMemoryAddress); /* Insert blocks into the tree. */ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram)); @@ -173,16 +172,21 @@ namespace ams::kern { 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. */ + /* Determine final total overhead size. */ 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; + + /* NOTE: Nintendo's kernel has layout [System, Management] but we have [Management, System]. This ensures the four UserPool regions are contiguous. */ + + /* Insert the system pool. */ + const uintptr_t system_pool_start = pool_partitions_start + total_overhead_size; + const size_t system_pool_size = unsafe_system_pool_start - system_pool_start; + InsertPoolPartitionRegionIntoBothTrees(system_pool_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); + + /* Insert the pool management region. */ + const uintptr_t pool_management_start = pool_partitions_start; 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); } else { /* On < 5.0.0, setup a legacy 2-pool layout for backwards compatibility. */ @@ -249,14 +253,18 @@ namespace ams::kern { total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size); } - /* Insert the secure pool. */ - InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, secure_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); - - /* Insert the pool management region. */ + /* Validate the true overhead size. */ MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= approximate_total_overhead_size); - const uintptr_t pool_management_start = pool_partitions_start + secure_pool_size; - const size_t pool_management_size = unsafe_memory_start - pool_management_start; + /* NOTE: Nintendo's kernel has layout [System, Management] but we have [Management, System]. This ensures the UserPool regions are contiguous. */ + + /* Insert the secure pool. */ + const uintptr_t secure_pool_start = unsafe_memory_start - secure_pool_size; + InsertPoolPartitionRegionIntoBothTrees(secure_pool_start, secure_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); + + /* Insert the pool management region. */ + const uintptr_t pool_management_start = pool_partitions_start; + const size_t pool_management_size = secure_pool_start - pool_management_start; MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= pool_management_size); u32 pool_management_attr = 0; diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp new file mode 100644 index 000000000..34631169a --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp @@ -0,0 +1,144 @@ +/* + * 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 size_t ReservedEarlyDramSize = 0x00080000; + + 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(ams::kern::MainMemoryAddress); + + /* 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); + + /* 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 = util::AlignDown(applet_pool_start - unsafe_system_pool_min_size, PageSize); + 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; + + /* Insert the application pool. */ + if (application_pool_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. */ + if (applet_pool_size > 0) { + 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. */ + if (unsafe_system_pool_size > 0) { + 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); + } + + /* Determine final total overhead size. */ + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size); + + /* NOTE: Nintendo's kernel has layout [System, Management] but we have [Management, System]. This ensures the four UserPool regions are contiguous. */ + + /* Insert the system pool. */ + const uintptr_t system_pool_start = pool_partitions_start + total_overhead_size; + const size_t system_pool_size = unsafe_system_pool_start - system_pool_start; + InsertPoolPartitionRegionIntoBothTrees(system_pool_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); + + /* Insert the pool management region. */ + const uintptr_t pool_management_start = pool_partitions_start; + 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); + } + + } + +} \ No newline at end of file diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 4bfa6a158..5b2b509fe 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -238,7 +238,7 @@ namespace ams::kern { } /* Generate random entropy. */ - KSystemControl::GenerateRandomBytes(m_entropy, sizeof(m_entropy)); + KSystemControl::GenerateRandom(m_entropy, util::size(m_entropy)); /* Clear remaining fields. */ m_num_running_threads = 0; diff --git a/libraries/libmesosphere/source/kern_k_system_control_base.cpp b/libraries/libmesosphere/source/kern_k_system_control_base.cpp new file mode 100644 index 000000000..a0cf3dfc0 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_system_control_base.cpp @@ -0,0 +1,295 @@ +/* + * 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 +#if defined(ATMOSPHERE_ARCH_ARM64) +#include +#endif + +namespace ams::kern { + + /* Initialization. */ + size_t KSystemControlBase::Init::GetRealMemorySize() { + return ams::kern::MainMemorySize; + } + + size_t KSystemControlBase::Init::GetIntendedMemorySize() { + return ams::kern::MainMemorySize; + } + + KPhysicalAddress KSystemControlBase::Init::GetKernelPhysicalBaseAddress(KPhysicalAddress base_address) { + const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize(); + 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 KSystemControlBase::Init::GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out) { + *out = { + .address = GetInteger(KSystemControl::Init::GetKernelPhysicalBaseAddress(ams::kern::MainMemoryAddress)) + KSystemControl::Init::GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax, + ._08 = 0, + }; + } + + + bool KSystemControlBase::Init::ShouldIncreaseThreadResourceLimit() { + return true; + } + + + size_t KSystemControlBase::Init::GetApplicationPoolSize() { + return 0; + } + + size_t KSystemControlBase::Init::GetAppletPoolSize() { + return 0; + } + + size_t KSystemControlBase::Init::GetMinimumNonSecureSystemPoolSize() { + return 0; + } + + u8 KSystemControlBase::Init::GetDebugLogUartPort() { + return 0; + } + + void KSystemControlBase::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) { + #if defined(ATMOSPHERE_ARCH_ARM64) + MESOSPHERE_INIT_ABORT_UNLESS((::ams::kern::arch::arm64::smc::CpuOn<0, false>(core_id, entrypoint, arg)) == 0); + #else + AMS_INFINITE_LOOP(); + #endif + } + + /* Randomness for Initialization. */ + void KSystemControlBase::Init::GenerateRandom(u64 *dst, size_t count) { + if (AMS_UNLIKELY(!s_initialized_random_generator)) { + const u64 seed = KHardwareTimer::GetTick(); + s_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); + s_initialized_random_generator = true; + } + + for (size_t i = 0; i < count; ++i) { + dst[i] = s_random_generator.GenerateRandomU64(); + } + } + + u64 KSystemControlBase::Init::GenerateRandomRange(u64 min, u64 max) { + if (AMS_UNLIKELY(!s_initialized_random_generator)) { + const u64 seed = KHardwareTimer::GetTick(); + s_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); + s_initialized_random_generator = true; + } + + return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); }); + } + + /* System Initialization. */ + void KSystemControlBase::InitializePhase1(bool skip_target_system) { + /* Initialize the rng, if we somehow haven't already. */ + if (AMS_UNLIKELY(!s_initialized_random_generator)) { + const u64 seed = KHardwareTimer::GetTick(); + s_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); + s_initialized_random_generator = true; + } + + /* Configure KTargetSystem, if we haven't already by an implementation SystemControl. */ + if (!skip_target_system) { + /* 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 KSystemControlBase::InitializePhase2() { + /* Initialize KTrace. */ + if constexpr (IsKTraceEnabled) { + const auto &ktrace = KMemoryLayout::GetKernelTraceBufferRegion(); + KTrace::Initialize(ktrace.GetAddress(), ktrace.GetSize()); + } + } + + u32 KSystemControlBase::GetCreateProcessMemoryPool() { + return KMemoryManager::Pool_System; + } + + /* Privileged Access. */ + void KSystemControlBase::ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) { + /* TODO */ + MESOSPHERE_UNUSED(out, address, mask, value); + MESOSPHERE_UNIMPLEMENTED(); + } + + Result KSystemControlBase::ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) { + MESOSPHERE_UNUSED(out, address, mask, value); + return svc::ResultNotImplemented(); + } + + /* Randomness. */ + void KSystemControlBase::GenerateRandom(u64 *dst, size_t count) { + KScopedInterruptDisable intr_disable; + KScopedSpinLock lk(s_random_lock); + + for (size_t i = 0; i < count; ++i) { + dst[i] = s_random_generator.GenerateRandomU64(); + } + } + + u64 KSystemControlBase::GenerateRandomRange(u64 min, u64 max) { + KScopedInterruptDisable intr_disable; + KScopedSpinLock lk(s_random_lock); + + return KSystemControlBase::GenerateUniformRange(min, max, [] ALWAYS_INLINE_LAMBDA () -> u64 { return s_random_generator.GenerateRandomU64(); }); + } + + u64 KSystemControlBase::GenerateRandomU64() { + KScopedInterruptDisable intr_disable; + KScopedSpinLock lk(s_random_lock); + + return s_random_generator.GenerateRandomU64(); + } + + void KSystemControlBase::SleepSystem() { + MESOSPHERE_LOG("SleepSystem() was called\n"); + } + + void KSystemControlBase::StopSystem(void *) { + MESOSPHERE_LOG("KSystemControlBase::StopSystem\n"); + AMS_INFINITE_LOOP(); + } + + /* User access. */ + #if defined(ATMOSPHERE_ARCH_ARM64) + void KSystemControlBase::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. */ + KSystemControl::CallSecureMonitorFromUserImpl(args); + + /* Make sure that we close any pages that we opened. */ + for (size_t i = 0; i < MaxMappedRegisters; i++) { + page_groups[i].Close(); + } + } + + void KSystemControlBase::CallSecureMonitorFromUserImpl(ams::svc::lp64::SecureMonitorArguments *args) { + /* By default, we don't actually support secure monitor, so just set args to a failure code. */ + args->r[0] = 1; + } + #endif + + /* Secure Memory. */ + size_t KSystemControlBase::CalculateRequiredSecureMemorySize(size_t size, u32 pool) { + MESOSPHERE_UNUSED(pool); + return size; + } + + Result KSystemControlBase::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) { + /* Ensure the size is aligned. */ + constexpr size_t Alignment = PageSize; + 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()); + + *out = KPageTable::GetHeapVirtualAddress(paddr); + return ResultSuccess(); + } + + void KSystemControlBase::FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool) { + /* Ensure the size is aligned. */ + constexpr size_t Alignment = PageSize; + MESOSPHERE_UNUSED(pool); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), Alignment)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, Alignment)); + + /* Close the secure region's pages. */ + Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize); + } + +} \ No newline at end of file diff --git a/libraries/libmesosphere/source/kern_kernel.cpp b/libraries/libmesosphere/source/kern_kernel.cpp index 090d5028b..a6e9b15a5 100644 --- a/libraries/libmesosphere/source/kern_kernel.cpp +++ b/libraries/libmesosphere/source/kern_kernel.cpp @@ -139,14 +139,20 @@ namespace ams::kern { PrintMemoryRegion(" InitPageTable", KMemoryLayout::GetKernelInitPageTableRegionPhysicalExtents()); PrintMemoryRegion(" MemoryPoolRegion", KMemoryLayout::GetKernelPoolPartitionRegionPhysicalExtents()); if (GetTargetFirmware() >= TargetFirmware_5_0_0) { - PrintMemoryRegion(" System", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents()); PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents()); - PrintMemoryRegion(" SystemUnsafe", KMemoryLayout::GetKernelSystemNonSecurePoolRegionPhysicalExtents()); - PrintMemoryRegion(" Applet", KMemoryLayout::GetKernelAppletPoolRegionPhysicalExtents()); - PrintMemoryRegion(" Application", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents()); + PrintMemoryRegion(" System", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents()); + if (KMemoryLayout::HasKernelSystemNonSecurePoolRegion()) { + PrintMemoryRegion(" SystemUnsafe", KMemoryLayout::GetKernelSystemNonSecurePoolRegionPhysicalExtents()); + } + if (KMemoryLayout::HasKernelAppletPoolRegion()) { + PrintMemoryRegion(" Applet", KMemoryLayout::GetKernelAppletPoolRegionPhysicalExtents()); + } + if (KMemoryLayout::HasKernelApplicationPoolRegion()) { + PrintMemoryRegion(" Application", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents()); + } } else { - PrintMemoryRegion(" Secure", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents()); PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents()); + PrintMemoryRegion(" Secure", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents()); PrintMemoryRegion(" Unsafe", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents()); } if constexpr (IsKTraceEnabled) { diff --git a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp index 07576c889..fe76b399b 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_cache.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_cache.cpp @@ -36,7 +36,7 @@ namespace ams::kern::svc { size_t remaining = size; while (remaining > 0) { /* Get a contiguous range to operate on. */ - KPageTableBase::MemoryRange contig_range = {}; + KPageTableBase::MemoryRange contig_range = { .address = Null, .size = 0 }; R_TRY(page_table.OpenMemoryRangeForProcessCacheOperation(std::addressof(contig_range), cur_address, aligned_end - cur_address)); /* Close the range when we're done operating on it. */ diff --git a/libraries/libstratosphere/Makefile b/libraries/libstratosphere/Makefile index 41d6fdb31..e8f2c5b54 100644 --- a/libraries/libstratosphere/Makefile +++ b/libraries/libstratosphere/Makefile @@ -123,6 +123,7 @@ $(OFILES) : $(GCH_FILES) $(OFILES_SRC) : $(HFILES_BIN) ams_environment_weak.o: CXXFLAGS += -fno-lto +hos_version_api_weak_for_unit_test.o: CXXFLAGS += -fno-lto pm_info_api_weak.o: CXXFLAGS += -fno-lto hos_stratosphere_api.o: CXXFLAGS += -fno-lto diff --git a/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp b/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp index 50bc3dce2..1ffb3cda6 100644 --- a/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp +++ b/libraries/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/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp b/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp index cf7145707..aa4446b76 100644 --- a/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_stratosphere_api.cpp @@ -48,6 +48,7 @@ namespace ams::hos { } + bool IsUnitTestProgramForSetVersion(); void InitializeVersionInternal(bool allow_approximate); void InitializeForStratosphere() { @@ -58,7 +59,7 @@ namespace ams::hos { hos::InitializeVersionInternal(CanAllowTemporaryApproximateVersion()); /* Check that we're running under mesosphere. */ - AMS_ABORT_UNLESS(svc::IsKernelMesosphere()); + AMS_ABORT_UNLESS(IsUnitTestProgramForSetVersion() || svc::IsKernelMesosphere()); } } diff --git a/libraries/libstratosphere/source/hos/hos_version_api.cpp b/libraries/libstratosphere/source/hos/hos_version_api.cpp index 49075d736..7abf07df0 100644 --- a/libraries/libstratosphere/source/hos/hos_version_api.cpp +++ b/libraries/libstratosphere/source/hos/hos_version_api.cpp @@ -61,51 +61,61 @@ namespace ams::hos { } + bool IsUnitTestProgramForSetVersion(); + void InitializeVersionInternal(bool allow_approximate) { - /* Get the current (and previous approximation of) target firmware. */ - hos::Version prev, current; - bool has_prev = false; - { - /* Acquire exclusive access to set hos version. */ - std::scoped_lock lk(g_hos_init_lock); + hos::Version current = hos::Version_Current; - /* Save the previous value of g_hos_version. */ - prev = g_hos_version; - has_prev = g_set_hos_version; - - /* Set hos version = exosphere api version target firmware. */ - g_hos_version = static_cast(GetExosphereApiInfo(allow_approximate).GetTargetFirmware()); - - /* Save the current value of g_hos_version. */ - current = g_hos_version; - - /* Note that we've set a previous hos version. */ + /* If we're unit testing, just set the version and move on. */ + if (IsUnitTestProgramForSetVersion()) { + g_hos_version = hos::Version_Current; g_set_hos_version = true; - } + } else { + /* Get the current (and previous approximation of) target firmware. */ + hos::Version prev; + bool has_prev = false; + { + /* Acquire exclusive access to set hos version. */ + std::scoped_lock lk(g_hos_init_lock); - /* Ensure that this is a hos version we can sanely *try* to run. */ - /* To be friendly, we will only require that we recognize the major and minor versions. */ - /* We can consider only recognizing major in the future, but micro seems safe to ignore as */ - /* there are no breaking IPC changes in minor updates. */ - { - constexpr u32 MaxMajor = (static_cast(hos::Version_Max) >> 24) & 0xFF; - constexpr u32 MaxMinor = (static_cast(hos::Version_Max) >> 16) & 0xFF; + /* Save the previous value of g_hos_version. */ + prev = g_hos_version; + has_prev = g_set_hos_version; - const u32 major = (static_cast(current) >> 24) & 0xFF; - const u32 minor = (static_cast(current) >> 16) & 0xFF; + /* Set hos version = exosphere api version target firmware. */ + g_hos_version = static_cast(GetExosphereApiInfo(allow_approximate).GetTargetFirmware()); - const bool is_safely_tryable_version = (current <= hos::Version_Max) || (major == MaxMajor && minor <= MaxMinor); - AMS_ABORT_UNLESS(is_safely_tryable_version); - } + /* Save the current value of g_hos_version. */ + current = g_hos_version; - /* Ensure that this is a hos version compatible with previous approximations. */ - if (has_prev) { - AMS_ABORT_UNLESS(current >= prev); + /* Note that we've set a previous hos version. */ + g_set_hos_version = true; + } - const u32 current_major = (static_cast(current) >> 24) & 0xFF; - const u32 prev_major = (static_cast(prev) >> 24) & 0xFF; + /* Ensure that this is a hos version we can sanely *try* to run. */ + /* To be friendly, we will only require that we recognize the major and minor versions. */ + /* We can consider only recognizing major in the future, but micro seems safe to ignore as */ + /* there are no breaking IPC changes in minor updates. */ + { + constexpr u32 MaxMajor = (static_cast(hos::Version_Max) >> 24) & 0xFF; + constexpr u32 MaxMinor = (static_cast(hos::Version_Max) >> 16) & 0xFF; - AMS_ABORT_UNLESS(current_major == prev_major); + const u32 major = (static_cast(current) >> 24) & 0xFF; + const u32 minor = (static_cast(current) >> 16) & 0xFF; + + const bool is_safely_tryable_version = (current <= hos::Version_Max) || (major == MaxMajor && minor <= MaxMinor); + AMS_ABORT_UNLESS(is_safely_tryable_version); + } + + /* Ensure that this is a hos version compatible with previous approximations. */ + if (has_prev) { + AMS_ABORT_UNLESS(current >= prev); + + const u32 current_major = (static_cast(current) >> 24) & 0xFF; + const u32 prev_major = (static_cast(prev) >> 24) & 0xFF; + + AMS_ABORT_UNLESS(current_major == prev_major); + } } /* Set the version for libnx. */ diff --git a/libraries/libstratosphere/source/hos/hos_version_api_weak_for_unit_test.cpp b/libraries/libstratosphere/source/hos/hos_version_api_weak_for_unit_test.cpp new file mode 100644 index 000000000..8f79a62be --- /dev/null +++ b/libraries/libstratosphere/source/hos/hos_version_api_weak_for_unit_test.cpp @@ -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 . + */ +#include + +namespace ams::hos { + + WEAK_SYMBOL bool IsUnitTestProgramForSetVersion() { + return false; + } + +} diff --git a/libraries/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp b/libraries/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp index 5f2d21285..4a36bb94e 100644 --- a/libraries/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp +++ b/libraries/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/libraries/libstratosphere/source/init/init_system_module.cpp b/libraries/libstratosphere/source/init/init_system_module.cpp index 75ccce4ac..0f64a1079 100644 --- a/libraries/libstratosphere/source/init/init_system_module.cpp +++ b/libraries/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/libraries/libvapours/include/vapours/defines.hpp b/libraries/libvapours/include/vapours/defines.hpp index bf9fc2123..66359fbd2 100644 --- a/libraries/libvapours/include/vapours/defines.hpp +++ b/libraries/libvapours/include/vapours/defines.hpp @@ -80,3 +80,26 @@ namespace ams::impl { #define AMS_UNUSED(...) ::ams::impl::UnusedImpl(__VA_ARGS__) #define AMS_INFINITE_LOOP() do { __asm__ __volatile__("" ::: "memory"); } while (1) + +#define AMS__NARG__(...) AMS__NARG_I_(__VA_ARGS__,AMS__RSEQ_N()) +#define AMS__NARG_I_(...) AMS__ARG_N(__VA_ARGS__) +#define AMS__ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define AMS__RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +#define AMS__VMACRO_(name, n) name##_##n +#define AMS__VMACRO(name, n) AMS__VMACRO_(name, n) +#define AMS_VMACRO(func, ...) AMS__VMACRO(func, AMS__NARG__(__VA_ARGS__)) (__VA_ARGS__) diff --git a/libraries/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp b/libraries/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp new file mode 100644 index 000000000..5160803a2 --- /dev/null +++ b/libraries/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/libraries/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp b/libraries/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp index e02784a84..ccd6db318 100644 --- a/libraries/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp +++ b/libraries/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 diff --git a/libraries/libvapours/include/vapours/svc/svc_version.hpp b/libraries/libvapours/include/vapours/svc/svc_version.hpp index 01b99ef7d..a80879fec 100644 --- a/libraries/libvapours/include/vapours/svc/svc_version.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_version.hpp @@ -58,7 +58,7 @@ namespace ams::svc { /* This is the highest SVC version supported by Atmosphere, to be updated on new kernel releases. */ /* NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. */ constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(13); - constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 3); + constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 4); constexpr inline u32 SupportedKernelVersion = EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion); diff --git a/libraries/libvapours/include/vapours/util/util_type_traits.hpp b/libraries/libvapours/include/vapours/util/util_type_traits.hpp index dea601140..448c18d69 100644 --- a/libraries/libvapours/include/vapours/util/util_type_traits.hpp +++ b/libraries/libvapours/include/vapours/util/util_type_traits.hpp @@ -26,4 +26,264 @@ namespace ams::util { struct ConstantInitializeTag final {}; constexpr inline const ConstantInitializeTag ConstantInitialize{}; + namespace impl { + + constexpr int ToIntegerForIsConstexprConstructible(...) { return {}; } + + template requires (std::is_constructible::value) + using ToIntegralConstantForIsConstexprConstructible = std::integral_constant; + + template::value> + std::true_type IsConstexprConstructibleImpl(int); + + template + std::false_type IsConstexprConstructibleImpl(long); + + template + consteval inline auto ConvertToLambdaForIsConstexprConstructible() { return [] { return T{}; }; } + + template + consteval inline auto ConvertToLambdaForIsConstexprConstructible() { return [] { return V; }; } + + namespace ambiguous_parse { + + struct AmbiguousParseHelperForIsConstexprConstructible { + + constexpr inline AmbiguousParseHelperForIsConstexprConstructible operator-() { return *this; } + + template + constexpr inline operator T() { + return T{}; + } + }; + + constexpr inline auto operator -(auto v, AmbiguousParseHelperForIsConstexprConstructible) { return v; } + + } + + #define AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(TYPE_OR_VALUE) [] { ::ams::util::impl::ambiguous_parse::AmbiguousParseHelperForIsConstexprConstructible p; auto v = (TYPE_OR_VALUE)-p; return v; } + + } + + template + using is_constexpr_constructible = decltype(impl::IsConstexprConstructibleImpl()...>(0)); + + template + using is_constexpr_constructible_by_values = decltype(impl::IsConstexprConstructibleImpl()...>(0)); + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_1(_1) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1>(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_2(_1, _2) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_3(_1, _2, _3) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_4(_1, _2, _3, _4) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_5(_1, _2, _3, _4, _5) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_6(_1, _2, _3, _4, _5, _6) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_7(_1, _2, _3, _4, _5, _6, _7) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_8(_1, _2, _3, _4, _5, _6, _7, _8) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_14) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_14), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_15) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + (decltype(::ams::util::impl::IsConstexprConstructibleImpl<_1, \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_2), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_3), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_4), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_5), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_6), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_7), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_8), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_9), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_10), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_11), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_12), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_13), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_14), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_15), \ + AMS_UTIL_IMPL_CONVERT_TV_TO_LAMBDA(_16) \ + >(0))::value) + + #define AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(...) AMS_VMACRO(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE, __VA_ARGS__) + + #if 0 + namespace test { + + struct S { + private: + int m_v; + public: + S() { } + + constexpr S(int v) : m_v() { } + constexpr S(int v, double z) : m_v(v) { } + }; + + consteval inline int test_constexpr_int() { return 0; } + inline int test_not_constexpr_int() { return 0; } + + static_assert(!AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S)); + static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, int)); + static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, 0)); + static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, test_constexpr_int())); + static_assert(!AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, test_not_constexpr_int())); + + static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, int, double)); + static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, int, 0.0)); + static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, 0, double)); + static_assert(AMS_UTIL_IS_CONSTEXPR_CONSTRUCTIBLE(S, 0, 0.0)); + + } + #endif + } diff --git a/mesosphere/Makefile b/mesosphere/Makefile index d168d278e..451a68a3e 100644 --- a/mesosphere/Makefile +++ b/mesosphere/Makefile @@ -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 diff --git a/mesosphere/build_mesosphere.py b/mesosphere/build_mesosphere.py index be51da4f0..5b9dd4f7b 100644 --- a/mesosphere/build_mesosphere.py +++ b/mesosphere/build_mesosphere.py @@ -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'' - try: - with open('ini.bin', 'rb') as f: - embedded_ini = f.read() - except: - pass + embedded_kips = b'' + num_kips = 0 + for kip_file in argv[4:]: + try: + 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(' +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template