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 class Final, template< typename...> class...Containers, typename...Types>\
+ struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };\
+ template class Final, template class List, typename...Ts>\
+ struct convert> { using type = typename append,TypeList...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+ template struct Nttp{};\
+ template\
+ constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+ template class...> struct NttpTemplateTypeList{};\
+ template