diff --git a/config/board/qemu/virt/board.mk b/config/board/qemu/virt/board.mk
new file mode 100644
index 00000000..752aad41
--- /dev/null
+++ b/config/board/qemu/virt/board.mk
@@ -0,0 +1,5 @@
+export ATMOSPHERE_DEFINES += -DATMOSPHERE_BOARD_QEMU_VIRT -D__SWITCH__
+export ATMOSPHERE_SETTINGS +=
+export ATMOSPHERE_CFLAGS +=
+export ATMOSPHERE_CXXFLAGS +=
+export ATMOSPHERE_ASFLAGS +=
\ No newline at end of file
diff --git a/config/common.mk b/config/common.mk
index 3fd7c1ca..460f7d9e 100644
--- a/config/common.mk
+++ b/config/common.mk
@@ -52,6 +52,21 @@ export ATMOSPHERE_OS_NAME := horizon
export ATMOSPHERE_CPU_EXTENSIONS :=
endif
+else ifeq ($(ATMOSPHERE_BOARD),qemu-virt)
+
+
+ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
+export ATMOSPHERE_ARCH_DIR := arm64
+export ATMOSPHERE_BOARD_DIR := qemu/virt
+export ATMOSPHERE_OS_DIR := horizon
+
+export ATMOSPHERE_ARCH_NAME := arm64
+export ATMOSPHERE_BOARD_NAME := qemu_virt
+export ATMOSPHERE_OS_NAME := horizon
+
+export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension
+endif
+
endif
ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57)
diff --git a/libmesosphere/Makefile b/libmesosphere/Makefile
index c60cc865..6a996f15 100644
--- a/libmesosphere/Makefile
+++ b/libmesosphere/Makefile
@@ -104,6 +104,18 @@ $(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \
ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
))
+$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_release, $(TARGET)_qemu_virt.a, \
+ ATMOSPHERE_BUILD_SETTINGS="" \
+))
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_debug, $(TARGET)_qemu_virt_debug.a, \
+ ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_DEBUGGING" \
+))
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, qemu_virt_audit, $(TARGET)_qemu_virt_audit.a, \
+ ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \
+))
+
#---------------------------------------------------------------------------------
-include $(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME).mk
diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp
index c06c4e30..35382437 100644
--- a/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp
+++ b/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp
@@ -30,6 +30,8 @@ namespace ams::kern::arch::arm64::cpu {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
constexpr inline size_t NumCores = 4;
+#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
+ constexpr inline size_t NumCores = 4;
#else
#error "Unknown Board for cpu::NumCores"
#endif
diff --git a/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp b/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp
index cbe6e1ea..6503b4e1 100644
--- a/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp
+++ b/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_name.hpp
@@ -36,6 +36,10 @@ namespace ams::kern::arch::arm64 {
KInterruptName_SecurePhysicalTimer = 29,
KInterruptName_NonSecurePhysicalTimer = 30,
KInterruptName_LegacyNIrq = 31,
+ #elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
+ KInterruptName_VirtualTimer = 27,
+ KInterruptName_SecurePhysicalTimer = 29,
+ KInterruptName_NonSecurePhysicalTimer = 30,
#endif
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
diff --git a/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp b/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp
index cc9404a4..0fb09b4e 100644
--- a/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp
+++ b/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp
@@ -44,13 +44,13 @@ namespace ams::kern::board::generic {
return ams::kern::svc::ResultNotImplemented();
}
- Result ALWAYS_INLINE Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) {
- MESOSPHERE_UNUSED(out_mapped_size, pg, device_address, device_perm, refresh_mappings);
+ Result ALWAYS_INLINE Map(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool is_aligned) {
+ MESOSPHERE_UNUSED(page_table, process_address, size, device_address, device_perm, is_aligned);
return ams::kern::svc::ResultNotImplemented();
}
- Result ALWAYS_INLINE Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) {
- MESOSPHERE_UNUSED(pg, device_address);
+ Result ALWAYS_INLINE Unmap(KProcessPageTable *page_table, KProcessAddress process_address, size_t size, KDeviceVirtualAddress device_address) {
+ MESOSPHERE_UNUSED(page_table, process_address, size, device_address);
return ams::kern::svc::ResultNotImplemented();
}
diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp b/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp
new file mode 100644
index 00000000..ec333546
--- /dev/null
+++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_cpu_map.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::kern::board::qemu::virt::impl::cpu {
+
+ /* Virtual to Physical core map. */
+ constexpr inline const s32 VirtualToPhysicalCoreMap[BITSIZEOF(u64)] = {
+ 0, 1, 2, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ };
+
+}
diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp
new file mode 100644
index 00000000..003f7457
--- /dev/null
+++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_layout.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::kern {
+
+ constexpr inline size_t MainMemorySize = 4_GB;
+ constexpr inline size_t MainMemorySizeMax = 8_GB;
+
+}
diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc
new file mode 100644
index 00000000..3d64f040
--- /dev/null
+++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_memory_region_device_types.inc
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/* All architectures must define NumBoardDeviceRegions. */
+constexpr inline const auto NumBoardDeviceRegions = 0;
+ /* UNUSED: .Derive(NumBoardDeviceRegions, 0); */
+
diff --git a/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp
new file mode 100644
index 00000000..d1d1fa21
--- /dev/null
+++ b/libmesosphere/include/mesosphere/board/qemu/virt/kern_k_system_control.hpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::kern {
+
+ struct InitialProcessBinaryLayout;
+
+}
+
+namespace ams::kern::board::qemu::virt {
+
+ class KSystemControl {
+ public:
+ class Init {
+ public:
+ /* Initialization. */
+ static size_t GetIntendedMemorySize();
+ static KPhysicalAddress GetKernelPhysicalBaseAddress(uintptr_t base_address);
+ static void GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out);
+ static bool ShouldIncreaseThreadResourceLimit();
+ static void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
+ static size_t GetApplicationPoolSize();
+ static size_t GetAppletPoolSize();
+ static size_t GetMinimumNonSecureSystemPoolSize();
+ static u8 GetDebugLogUartPort();
+
+ /* Randomness. */
+ static void GenerateRandomBytes(void *dst, size_t size);
+ static u64 GenerateRandomRange(u64 min, u64 max);
+ };
+ public:
+ /* Initialization. */
+ static NOINLINE void InitializePhase1();
+ static NOINLINE void InitializePhase2();
+ static NOINLINE u32 GetCreateProcessMemoryPool();
+
+ /* Randomness. */
+ static void GenerateRandomBytes(void *dst, size_t size);
+ static u64 GenerateRandomRange(u64 min, u64 max);
+ static u64 GenerateRandomU64();
+
+ /* Privileged Access. */
+ static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
+ static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value);
+
+ static ALWAYS_INLINE u32 ReadRegisterPrivileged(ams::svc::PhysicalAddress address) {
+ u32 v;
+ ReadWriteRegisterPrivileged(std::addressof(v), address, 0x00000000u, 0);
+ return v;
+ }
+
+ static ALWAYS_INLINE void WriteRegisterPrivileged(ams::svc::PhysicalAddress address, u32 value) {
+ u32 v;
+ ReadWriteRegisterPrivileged(std::addressof(v), address, 0xFFFFFFFFu, value);
+ }
+
+ /* Power management. */
+ static void SleepSystem();
+ static NORETURN void StopSystem(void *arg = nullptr);
+
+ /* User access. */
+ static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
+
+ /* Secure Memory. */
+ static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
+ static Result AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool);
+ static void FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool);
+ };
+
+}
\ No newline at end of file
diff --git a/libmesosphere/include/mesosphere/kern_debug_log.hpp b/libmesosphere/include/mesosphere/kern_debug_log.hpp
index 8253b1a2..51b83596 100644
--- a/libmesosphere/include/mesosphere/kern_debug_log.hpp
+++ b/libmesosphere/include/mesosphere/kern_debug_log.hpp
@@ -43,6 +43,8 @@ namespace ams::kern {
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX
#define MESOSPHERE_DEBUG_LOG_USE_UART
+ #elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
+ #define MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING
#else
#error "Unknown board for Default Debug Log Source"
#endif
diff --git a/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp
index 66ee75ab..5ac2a590 100644
--- a/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp
+++ b/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp
@@ -20,6 +20,8 @@
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include
+#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
+ #include
#else
#error "Unknown board for KMemoryLayout"
#endif
diff --git a/libmesosphere/include/mesosphere/kern_select_cpu.hpp b/libmesosphere/include/mesosphere/kern_select_cpu.hpp
index 8cf08533..799a2fb3 100644
--- a/libmesosphere/include/mesosphere/kern_select_cpu.hpp
+++ b/libmesosphere/include/mesosphere/kern_select_cpu.hpp
@@ -39,6 +39,16 @@
}
+#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
+
+ #include
+
+ namespace ams::kern::cpu {
+
+ using namespace ams::kern::board::qemu::virt::impl::cpu;
+
+ }
+
#else
#error "Unknown board for CPU Map"
#endif
diff --git a/libmesosphere/include/mesosphere/kern_select_system_control.hpp b/libmesosphere/include/mesosphere/kern_select_system_control.hpp
index cb1bb4d9..0ea19592 100644
--- a/libmesosphere/include/mesosphere/kern_select_system_control.hpp
+++ b/libmesosphere/include/mesosphere/kern_select_system_control.hpp
@@ -23,6 +23,13 @@
using ams::kern::board::nintendo::nx::KSystemControl;
}
+#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
+ #include
+
+ namespace ams::kern {
+ using ams::kern::board::qemu::virt::KSystemControl;
+ }
+
#else
#error "Unknown board for KSystemControl"
#endif
diff --git a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp
index 112524fa..2ec4cbe2 100644
--- a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp
+++ b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp
@@ -25,7 +25,23 @@ namespace ams::kern::board::nintendo::nx::smc {
};
enum UserFunctionId : u32 {
- UserFunctionId_SetConfig = 0xC3000401,
+ UserFunctionId_SetConfig = 0xC3000401,
+ UserFunctionId_GetConfigUser = 0xC3000002,
+ UserFunctionId_GetResult = 0xC3000003,
+ UserFunctionId_GetResultData = 0xC3000404,
+ UserFunctionId_ModularExponentiate = 0xC3000E05,
+ UserFunctionId_GenerateRandomBytes = 0xC3000006,
+ UserFunctionId_GenerateAesKek = 0xC3000007,
+ UserFunctionId_LoadAesKey = 0xC3000008,
+ UserFunctionId_ComputeAes = 0xC3000009,
+ UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
+ UserFunctionId_ComputeCmac = 0xC300040B,
+ UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
+ UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
+ UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
+ UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
+ UserFunctionId_LoadPreparedAesKey = 0xC3000011,
+ UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
};
enum FunctionId : u32 {
diff --git a/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp b/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp
new file mode 100644
index 00000000..53526eec
--- /dev/null
+++ b/libmesosphere/source/board/qemu/virt/kern_k_system_control.cpp
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "kern_secure_monitor.hpp"
+
+namespace ams::kern::board::qemu::virt {
+
+ namespace {
+
+ constexpr uintptr_t DramPhysicalAddress = 0x40000000;
+ constexpr size_t SecureAlignment = 128_KB;
+
+ /* Global variables for secure memory. */
+ constexpr size_t SecureAppletMemorySize = 4_MB;
+ constinit KSpinLock g_secure_applet_lock;
+ constinit bool g_secure_applet_memory_used = false;
+ constinit KVirtualAddress g_secure_applet_memory_address = Null;
+
+ constinit KSpinLock g_secure_region_lock;
+ constinit bool g_secure_region_used = false;
+ constinit KPhysicalAddress g_secure_region_phys_addr = Null;
+ constinit size_t g_secure_region_size = 0;
+
+ /* Global variables for randomness. */
+ constinit bool g_initialized_random_generator;
+ constinit util::TinyMT g_random_generator;
+ constinit KSpinLock g_random_lock;
+
+ ALWAYS_INLINE u64 GenerateRandomU64FromGenerator() {
+ return g_random_generator.GenerateRandomU64();
+ }
+
+ template
+ ALWAYS_INLINE u64 GenerateUniformRange(u64 min, u64 max, F f) {
+ /* Handle the case where the difference is too large to represent. */
+ if (max == std::numeric_limits::max() && min == std::numeric_limits::min()) {
+ return f();
+ }
+
+ /* Iterate until we get a value in range. */
+ const u64 range_size = ((max + 1) - min);
+ const u64 effective_max = (std::numeric_limits::max() / range_size) * range_size;
+ while (true) {
+ if (const u64 rnd = f(); rnd < effective_max) {
+ return min + (rnd % range_size);
+ }
+ }
+ }
+
+
+ /* TODO */
+
+ ALWAYS_INLINE size_t GetRealMemorySizeForInit() {
+ return 4_GB;
+ }
+
+ bool SetSecureRegion(KPhysicalAddress phys_addr, size_t size) {
+ /* Ensure address and size are aligned. */
+ if (!util::IsAligned(GetInteger(phys_addr), SecureAlignment)) {
+ return false;
+ }
+ if (!util::IsAligned(size, SecureAlignment)) {
+ return false;
+ }
+
+ /* Disable interrupts and acquire the secure region lock. */
+ KScopedInterruptDisable di;
+ KScopedSpinLock lk(g_secure_region_lock);
+
+ /* If size is non-zero, we're allocating the secure region. Otherwise, we're freeing it. */
+ if (size != 0) {
+ /* Verify that the secure region is free. */
+ if (g_secure_region_used) {
+ return false;
+ }
+
+ /* Set the secure region. */
+ g_secure_region_used = true;
+ g_secure_region_phys_addr = phys_addr;
+ g_secure_region_size = size;
+ } else {
+ /* Verify that the secure region is in use. */
+ if (!g_secure_region_used) {
+ return false;
+ }
+
+ /* Verify that the address being freed is the secure region. */
+ if (phys_addr != g_secure_region_phys_addr) {
+ return false;
+ }
+
+ /* Clear the secure region. */
+ g_secure_region_used = false;
+ g_secure_region_phys_addr = Null;
+ g_secure_region_size = 0;
+ }
+
+ // /* Configure the carveout with the secure monitor. */
+ // smc::ConfigureCarveout(1, GetInteger(phys_addr), size);
+
+ return true;
+ }
+
+ Result AllocateSecureMemoryForApplet(KVirtualAddress *out, size_t size) {
+ /* Verify that the size is valid. */
+ R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize());
+ R_UNLESS(size <= SecureAppletMemorySize, svc::ResultOutOfMemory());
+
+ /* Disable interrupts and acquire the secure applet lock. */
+ KScopedInterruptDisable di;
+ KScopedSpinLock lk(g_secure_applet_lock);
+
+ /* Check that memory is reserved for secure applet use. */
+ MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address != Null);
+
+ /* Verify that the secure applet memory isn't already being used. */
+ R_UNLESS(!g_secure_applet_memory_used, svc::ResultOutOfMemory());
+
+ /* Return the secure applet memory. */
+ g_secure_applet_memory_used = true;
+ *out = g_secure_applet_memory_address;
+
+ return ResultSuccess();
+ }
+
+ void FreeSecureMemoryForApplet(KVirtualAddress address, size_t size) {
+ /* Disable interrupts and acquire the secure applet lock. */
+ KScopedInterruptDisable di;
+ KScopedSpinLock lk(g_secure_applet_lock);
+
+ /* Verify that the memory being freed is correct. */
+ MESOSPHERE_ABORT_UNLESS(address == g_secure_applet_memory_address);
+ MESOSPHERE_ABORT_UNLESS(size <= SecureAppletMemorySize);
+ MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize));
+ MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_used);
+
+ /* Release the secure applet memory. */
+ g_secure_applet_memory_used = false;
+ }
+
+ void EnsureRandomGeneratorSeeded() {
+ if (AMS_UNLIKELY(!g_initialized_random_generator)) {
+ u64 seed = UINT64_C(0xF5F5F5F5F5F5F5F5);
+ g_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32));
+ g_initialized_random_generator = true;
+ }
+ }
+
+ }
+
+ /* Initialization. */
+ size_t KSystemControl::Init::GetIntendedMemorySize() {
+ return 4_GB;
+ }
+
+ KPhysicalAddress KSystemControl::Init::GetKernelPhysicalBaseAddress(uintptr_t base_address) {
+ const size_t real_dram_size = GetRealMemorySizeForInit();
+ const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
+ if (intended_dram_size * 2 < real_dram_size) {
+ return base_address;
+ } else {
+ return base_address + ((real_dram_size - intended_dram_size) / 2);
+ }
+ }
+
+ void KSystemControl::Init::GetInitialProcessBinaryLayout(InitialProcessBinaryLayout *out) {
+ *out = {
+ .address = GetInteger(GetKernelPhysicalBaseAddress(DramPhysicalAddress)) + GetIntendedMemorySize() - KTraceBufferSize - InitialProcessBinarySizeMax,
+ ._08 = 0,
+ };
+ }
+
+
+ bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
+ return true;
+ }
+
+
+ size_t KSystemControl::Init::GetApplicationPoolSize() {
+ /* Get the base pool size. */
+ const size_t base_pool_size = 3285_MB;
+
+ /* Return (possibly) adjusted size. */
+ return base_pool_size;
+ }
+
+ size_t KSystemControl::Init::GetAppletPoolSize() {
+ /* Get the base pool size. */
+ const size_t base_pool_size = 507_MB;
+
+ /* Return (possibly) adjusted size. */
+ constexpr size_t ExtraSystemMemoryForAtmosphere = 40_MB;
+ return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize;
+ }
+
+ size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
+ return 0x29C8000;
+ }
+
+ void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
+ smc::init::CpuOn(core_id, entrypoint, arg);
+ }
+
+ /* Randomness for Initialization. */
+ void KSystemControl::Init::GenerateRandomBytes(void *dst, size_t size) {
+ EnsureRandomGeneratorSeeded();
+
+ u8 *dst_8 = static_cast(dst);
+ while (size > 0) {
+ const u64 random = GenerateRandomU64FromGenerator();
+ std::memcpy(dst_8, std::addressof(random), std::min(size, sizeof(u64)));
+ size -= std::min(size, sizeof(u64));
+ }
+ }
+
+ u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) {
+ EnsureRandomGeneratorSeeded();
+
+ return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
+ }
+
+ /* System Initialization. */
+ void KSystemControl::InitializePhase1() {
+ /* Set IsDebugMode. */
+ {
+ KTargetSystem::SetIsDebugMode(true);
+
+ /* If debug mode, we want to initialize uart logging. */
+ KTargetSystem::EnableDebugLogging(true);
+ KDebugLog::Initialize();
+ }
+
+ /* Set Kernel Configuration. */
+ {
+ KTargetSystem::EnableDebugMemoryFill(false);
+ KTargetSystem::EnableUserExceptionHandlers(true);
+ KTargetSystem::EnableDynamicResourceLimits(true);
+ KTargetSystem::EnableUserPmuAccess(false);
+ }
+
+ /* Set Kernel Debugging. */
+ {
+ /* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
+ /* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
+ KTargetSystem::EnableKernelDebugging(true);
+ }
+
+ /* System ResourceLimit initialization. */
+ {
+ /* Construct the resource limit object. */
+ KResourceLimit &sys_res_limit = Kernel::GetSystemResourceLimit();
+ KAutoObject::Create(std::addressof(sys_res_limit));
+ sys_res_limit.Initialize();
+
+ /* Set the initial limits. */
+ const auto [total_memory_size, kernel_memory_size] = KMemoryLayout::GetTotalAndKernelMemorySizes();
+ const auto &slab_counts = init::GetSlabResourceCounts();
+ MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size));
+ MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread));
+ MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_EventCountMax, slab_counts.num_KEvent));
+ MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_TransferMemoryCountMax, slab_counts.num_KTransferMemory));
+ MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_SessionCountMax, slab_counts.num_KSession));
+
+ /* Reserve system memory. */
+ MESOSPHERE_ABORT_UNLESS(sys_res_limit.Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, kernel_memory_size));
+ }
+ }
+
+ void KSystemControl::InitializePhase2() {
+ /* Reserve secure applet memory. */
+ if (GetTargetFirmware() >= TargetFirmware_5_0_0) {
+ MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address == Null);
+ MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletMemorySize));
+
+ constexpr auto SecureAppletAllocateOption = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront);
+ const KPhysicalAddress secure_applet_memory_phys_addr = Kernel::GetMemoryManager().AllocateAndOpenContinuous(SecureAppletMemorySize / PageSize, 1, SecureAppletAllocateOption);
+ MESOSPHERE_ABORT_UNLESS(secure_applet_memory_phys_addr != Null);
+
+ g_secure_applet_memory_address = KMemoryLayout::GetLinearVirtualAddress(secure_applet_memory_phys_addr);
+ }
+
+ /* Initialize KTrace. */
+ if constexpr (IsKTraceEnabled) {
+ const auto &ktrace = KMemoryLayout::GetKernelTraceBufferRegion();
+ KTrace::Initialize(ktrace.GetAddress(), ktrace.GetSize());
+ }
+ }
+
+ u32 KSystemControl::GetCreateProcessMemoryPool() {
+ return KMemoryManager::Pool_Unsafe;
+ }
+
+ /* Privileged Access. */
+ void KSystemControl::ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
+ MESOSPHERE_UNUSED(out, address, mask, value);
+ MESOSPHERE_UNIMPLEMENTED();
+ }
+
+ Result KSystemControl::ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
+ MESOSPHERE_UNUSED(out, address, mask, value);
+ MESOSPHERE_UNIMPLEMENTED();
+ }
+
+ /* Randomness. */
+ void KSystemControl::GenerateRandomBytes(void *dst, size_t size) {
+ KScopedInterruptDisable intr_disable;
+ KScopedSpinLock lk(g_random_lock);
+
+ u8 *dst_8 = static_cast(dst);
+ while (size > 0) {
+ const u64 random = GenerateRandomU64FromGenerator();
+ std::memcpy(dst_8, std::addressof(random), std::min(size, sizeof(u64)));
+ size -= std::min(size, sizeof(u64));
+ }
+ }
+
+ u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
+ KScopedInterruptDisable intr_disable;
+ KScopedSpinLock lk(g_random_lock);
+
+ return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
+ }
+
+ u64 KSystemControl::GenerateRandomU64() {
+ KScopedInterruptDisable intr_disable;
+ KScopedSpinLock lk(g_random_lock);
+
+ return GenerateRandomU64FromGenerator();
+ }
+
+ void KSystemControl::SleepSystem() {
+ MESOSPHERE_LOG("SleepSystem() was called\n");
+ MESOSPHERE_UNIMPLEMENTED();
+ }
+
+ void KSystemControl::StopSystem(void *arg) {
+ MESOSPHERE_UNUSED(arg);
+ AMS_INFINITE_LOOP();
+ }
+
+ /* User access. */
+ void KSystemControl::CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
+ /* Get the function id for the current call. */
+ u64 function_id = args->r[0];
+
+ /* We'll need to map in pages if arguments are pointers. Prepare page groups to do so. */
+ auto &page_table = GetCurrentProcess().GetPageTable();
+ auto *bim = page_table.GetBlockInfoManager();
+
+ constexpr size_t MaxMappedRegisters = 7;
+ std::array page_groups = { KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), KPageGroup(bim), };
+
+ for (size_t i = 0; i < MaxMappedRegisters; i++) {
+ const size_t reg_id = i + 1;
+ if (function_id & (1ul << (8 + reg_id))) {
+ /* Create and open a new page group for the address. */
+ KVirtualAddress virt_addr = args->r[reg_id];
+
+ if (R_SUCCEEDED(page_table.MakeAndOpenPageGroup(std::addressof(page_groups[i]), util::AlignDown(GetInteger(virt_addr), PageSize), 1, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None))) {
+ /* Translate the virtual address to a physical address. */
+ const auto it = page_groups[i].begin();
+ MESOSPHERE_ASSERT(it != page_groups[i].end());
+ MESOSPHERE_ASSERT(it->GetNumPages() == 1);
+
+ args->r[reg_id] = GetInteger(it->GetAddress()) | (GetInteger(virt_addr) & (PageSize - 1));
+ } else {
+ /* If we couldn't map, we should clear the address. */
+ args->r[reg_id] = 0;
+ }
+ }
+ }
+
+ /* Invoke the secure monitor. */
+ smc::CallSecureMonitorFromUser(args);
+
+ /* Make sure that we close any pages that we opened. */
+ for (size_t i = 0; i < MaxMappedRegisters; i++) {
+ page_groups[i].Close();
+ }
+ }
+
+ /* Secure Memory. */
+ size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
+ if (pool == KMemoryManager::Pool_Applet) {
+ return 0;
+ }
+ return size;
+ }
+
+ Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) {
+ /* Applet secure memory is handled separately. */
+ if (pool == KMemoryManager::Pool_Applet) {
+ return AllocateSecureMemoryForApplet(out, size);
+ }
+
+ /* Ensure the size is aligned. */
+ const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment);
+ R_UNLESS(util::IsAligned(size, alignment), svc::ResultInvalidSize());
+
+ /* Allocate the memory. */
+ const size_t num_pages = size / PageSize;
+ const KPhysicalAddress paddr = Kernel::GetMemoryManager().AllocateAndOpenContinuous(num_pages, alignment / PageSize, KMemoryManager::EncodeOption(static_cast(pool), KMemoryManager::Direction_FromFront));
+ R_UNLESS(paddr != Null, svc::ResultOutOfMemory());
+
+ /* Ensure we don't leak references to the memory on error. */
+ auto mem_guard = SCOPE_GUARD { Kernel::GetMemoryManager().Close(paddr, num_pages); };
+
+ /* If the memory isn't already secure, set it as secure. */
+ if (pool != KMemoryManager::Pool_System) {
+ /* Set the secure region. */
+ R_UNLESS(SetSecureRegion(paddr, size), svc::ResultOutOfMemory());
+ }
+
+ /* We succeeded. */
+ mem_guard.Cancel();
+ *out = KPageTable::GetHeapVirtualAddress(paddr);
+ return ResultSuccess();
+ }
+
+ void KSystemControl::FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool) {
+ /* Applet secure memory is handled separately. */
+ if (pool == KMemoryManager::Pool_Applet) {
+ return FreeSecureMemoryForApplet(address, size);
+ }
+
+ /* Ensure the size is aligned. */
+ const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment);
+ MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), alignment));
+ MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, alignment));
+
+ /* If the memory isn't secure system, reset the secure region. */
+ if (pool != KMemoryManager::Pool_System) {
+ /* Check that the size being freed is the current secure region size. */
+ MESOSPHERE_ABORT_UNLESS(g_secure_region_size == size);
+
+ /* Get the physical address. */
+ const KPhysicalAddress paddr = KPageTable::GetHeapPhysicalAddress(address);
+ MESOSPHERE_ABORT_UNLESS(paddr != Null);
+
+ /* Check that the memory being freed is the current secure region. */
+ MESOSPHERE_ABORT_UNLESS(paddr == g_secure_region_phys_addr);
+
+ /* Free the secure region. */
+ MESOSPHERE_ABORT_UNLESS(SetSecureRegion(paddr, 0));
+ }
+
+ /* Close the secure region's pages. */
+ Kernel::GetMemoryManager().Close(KPageTable::GetHeapPhysicalAddress(address), size / PageSize);
+ }
+
+}
\ No newline at end of file
diff --git a/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp
new file mode 100644
index 00000000..c4fa1c38
--- /dev/null
+++ b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "kern_secure_monitor.hpp"
+
+namespace ams::kern::board::qemu::virt::smc {
+
+ namespace {
+
+ struct SecureMonitorArguments {
+ u64 x[8];
+ };
+
+ enum UserFunctionId : u32 {
+ UserFunctionId_SetConfig = 0xC3000401,
+ UserFunctionId_GetConfig = 0xC3000002,
+ UserFunctionId_GetResult = 0xC3000003,
+ UserFunctionId_GetResultData = 0xC3000404,
+ UserFunctionId_ModularExponentiate = 0xC3000E05,
+ UserFunctionId_GenerateRandomBytes = 0xC3000006,
+ UserFunctionId_GenerateAesKek = 0xC3000007,
+ UserFunctionId_LoadAesKey = 0xC3000008,
+ UserFunctionId_ComputeAes = 0xC3000009,
+ UserFunctionId_GenerateSpecificAesKey = 0xC300000A,
+ UserFunctionId_ComputeCmac = 0xC300040B,
+ UserFunctionId_ReencryptDeviceUniqueData = 0xC300D60C,
+ UserFunctionId_DecryptDeviceUniqueData = 0xC300100D,
+ UserFunctionId_ModularExponentiateByStorageKey = 0xC300060F,
+ UserFunctionId_PrepareEsDeviceUniqueKey = 0xC3000610,
+ UserFunctionId_LoadPreparedAesKey = 0xC3000011,
+ UserFunctionId_PrepareEsCommonTitleKey = 0xC3000012,
+ };
+
+ enum FunctionId : u32 {
+ FunctionId_CpuSuspend = 0xC4000001,
+ FunctionId_CpuOff = 0x84000002,
+ FunctionId_CpuOn = 0xC4000003,
+ };
+
+ void CallPrivilegedSecureMonitorFunction(SecureMonitorArguments &args) {
+ /* Load arguments into registers. */
+ register u64 x0 asm("x0") = args.x[0];
+ register u64 x1 asm("x1") = args.x[1];
+ register u64 x2 asm("x2") = args.x[2];
+ register u64 x3 asm("x3") = args.x[3];
+ register u64 x4 asm("x4") = args.x[4];
+ register u64 x5 asm("x5") = args.x[5];
+ register u64 x6 asm("x6") = args.x[6];
+ register u64 x7 asm("x7") = args.x[7];
+
+ /* Actually make the call. */
+ {
+ /* Disable interrupts while making the call. */
+ KScopedInterruptDisable intr_disable;
+
+ {
+ /* Backup the current thread pointer. */
+ const uintptr_t current_thread_pointer_value = cpu::GetCurrentThreadPointerValue();
+
+ __asm__ __volatile__("smc #0"
+ : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
+ :
+ : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
+ );
+
+ /* Restore the current thread pointer into X18. */
+ cpu::SetCurrentThreadPointerValue(current_thread_pointer_value);
+
+ /* Store arguments to output. */
+ args.x[0] = x0;
+ args.x[1] = x1;
+ args.x[2] = x2;
+ args.x[3] = x3;
+ args.x[4] = x4;
+ args.x[5] = x5;
+ args.x[6] = x6;
+ args.x[7] = x7;
+ }
+ }
+ }
+
+ void CallPrivilegedSecureMonitorFunctionForInit(SecureMonitorArguments &args) {
+ /* Load arguments into registers. */
+ register u64 x0 asm("x0") = args.x[0];
+ register u64 x1 asm("x1") = args.x[1];
+ register u64 x2 asm("x2") = args.x[2];
+ register u64 x3 asm("x3") = args.x[3];
+ register u64 x4 asm("x4") = args.x[4];
+ register u64 x5 asm("x5") = args.x[5];
+ register u64 x6 asm("x6") = args.x[6];
+ register u64 x7 asm("x7") = args.x[7];
+
+ /* Actually make the call. */
+ __asm__ __volatile__("smc #0"
+ : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7)
+ :
+ : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory"
+ );
+
+ /* Store arguments to output. */
+ args.x[0] = x0;
+ args.x[1] = x1;
+ args.x[2] = x2;
+ args.x[3] = x3;
+ args.x[4] = x4;
+ args.x[5] = x5;
+ args.x[6] = x6;
+ args.x[7] = x7;
+ }
+
+ /* Global lock for generate random bytes. */
+ KSpinLock g_generate_random_lock;
+
+ }
+
+ /* SMC functionality needed for init. */
+ namespace init {
+
+ void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
+ SecureMonitorArguments args = { FunctionId_CpuOn, core_id, entrypoint, arg };
+ CallPrivilegedSecureMonitorFunctionForInit(args);
+ }
+
+ }
+
+ void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
+ SecureMonitorArguments args = { FunctionId_CpuOn, core_id, static_cast(entrypoint), static_cast(arg) };
+ CallPrivilegedSecureMonitorFunction(args);
+ MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success));
+ }
+
+ void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) {
+ MESOSPHERE_LOG("Received SMC [%p %p %p %p %p %p %p %p] from %s\n", reinterpret_cast(args->r[0]), reinterpret_cast(args->r[1]), reinterpret_cast(args->r[2]), reinterpret_cast(args->r[3]), reinterpret_cast(args->r[4]), reinterpret_cast(args->r[5]), reinterpret_cast(args->r[6]), reinterpret_cast(args->r[7]), GetCurrentProcess().GetName());
+
+ switch (args->r[0]) {
+ case UserFunctionId_GetConfig:
+ {
+ switch (static_cast(args->r[1])) {
+ case ConfigItem::ExosphereApiVersion:
+ args->r[1] = (static_cast(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
+ (static_cast(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
+ (static_cast(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
+ (static_cast(13) << 32) |
+ (static_cast(GetTargetFirmware()) << 0);
+ break;
+ default:
+ MESOSPHERE_PANIC("Unhandled GetConfig\n");
+ }
+
+ args->r[0] = static_cast(SmcResult::Success);
+ }
+ break;
+ default:
+ MESOSPHERE_PANIC("Unhandled SMC [%p %p %p %p %p %p %p %p]", reinterpret_cast(args->r[0]), reinterpret_cast(args->r[1]), reinterpret_cast(args->r[2]), reinterpret_cast(args->r[3]), reinterpret_cast(args->r[4]), reinterpret_cast(args->r[5]), reinterpret_cast(args->r[6]), reinterpret_cast(args->r[7]));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp
new file mode 100644
index 00000000..86962400
--- /dev/null
+++ b/libmesosphere/source/board/qemu/virt/kern_secure_monitor.hpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::kern::board::qemu::virt::smc {
+
+ enum class ConfigItem : u32 {
+ /* Standard config items. */
+ DisableProgramVerification = 1,
+ DramId = 2,
+ SecurityEngineIrqNumber = 3,
+ Version = 4,
+ HardwareType = 5,
+ IsRetail = 6,
+ IsRecoveryBoot = 7,
+ DeviceId = 8,
+ BootReason = 9,
+ MemoryMode = 10,
+ IsDebugMode = 11,
+ KernelConfiguration = 12,
+ IsChargerHiZModeEnabled = 13,
+ IsQuest = 14,
+ RegulatorType = 15,
+ DeviceUniqueKeyGeneration = 16,
+ Package2Hash = 17,
+
+ /* Extension config items for exosphere. */
+ ExosphereApiVersion = 65000,
+ ExosphereNeedsReboot = 65001,
+ ExosphereNeedsShutdown = 65002,
+ ExosphereGitCommitHash = 65003,
+ ExosphereHasRcmBugPatch = 65004,
+ ExosphereBlankProdInfo = 65005,
+ ExosphereAllowCalWrites = 65006,
+ ExosphereEmummcType = 65007,
+ ExospherePayloadAddress = 65008,
+ ExosphereLogConfiguration = 65009,
+ ExosphereForceEnableUsb30 = 65010,
+ ExosphereSupportedHosVersion = 65011,
+ };
+
+ enum class SmcResult {
+ Success = 0,
+ NotImplemented = 1,
+ InvalidArgument = 2,
+ InProgress = 3,
+ NoAsyncOperation = 4,
+ InvalidAsyncOperation = 5,
+ NotPermitted = 6,
+ };
+
+ void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
+
+ void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args);
+
+ namespace init {
+
+ void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
+
+ }
+
+}
\ No newline at end of file
diff --git a/libmesosphere/source/kern_debug_log.cpp b/libmesosphere/source/kern_debug_log.cpp
index 6ff3df94..5f296b65 100644
--- a/libmesosphere/source/kern_debug_log.cpp
+++ b/libmesosphere/source/kern_debug_log.cpp
@@ -32,6 +32,9 @@ namespace ams::kern {
return;
}
+ #if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
+ KDebugLogImpl::PutStringBySemihosting(str);
+ #else
while (*str) {
/* Get a character. */
const char c = *(str++);
@@ -44,6 +47,7 @@ namespace ams::kern {
}
KDebugLogImpl::Flush();
+ #endif
}
#if defined(MESOSPHERE_ENABLE_DEBUG_PRINT)
@@ -54,6 +58,11 @@ namespace ams::kern {
return ResultSuccess();
}
+ #if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
+ /* TODO: should we do this properly? */
+ KDebugLogImpl::PutStringBySemihosting(user_str.GetUnsafePointer());
+ MESOSPHERE_UNUSED(len);
+ #else
for (size_t i = 0; i < len; ++i) {
/* Get a character. */
char c;
@@ -67,6 +76,7 @@ namespace ams::kern {
}
KDebugLogImpl::Flush();
+ #endif
return ResultSuccess();
}
diff --git a/libmesosphere/source/kern_debug_log_impl.arch.arm64.s b/libmesosphere/source/kern_debug_log_impl.arch.arm64.s
new file mode 100644
index 00000000..e7d4423e
--- /dev/null
+++ b/libmesosphere/source/kern_debug_log_impl.arch.arm64.s
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/* ams::kern::KDebugLogImpl::PutStringBySemihosting(const char *str) */
+.section .text._ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc, "ax", %progbits
+.global _ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc
+.type _ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc, %function
+.balign 0x10
+_ZN3ams4kern13KDebugLogImpl22PutStringBySemihostingEPKc:
+ mov x1, x0
+ mov x0, #0x4
+ hlt #0xF000
+ ret
+
diff --git a/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp b/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp
new file mode 100644
index 00000000..a71b904d
--- /dev/null
+++ b/libmesosphere/source/kern_debug_log_impl.board.qemu_virt.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "kern_debug_log_impl.hpp"
+
+namespace ams::kern {
+
+#if defined(MESOSPHERE_DEBUG_LOG_USE_SEMIHOSTING)
+
+ bool KDebugLogImpl::Initialize() {
+ return true;
+ }
+
+ void KDebugLogImpl::PutChar(char c) {
+ /* TODO */
+ AMS_UNUSED(c);
+ }
+
+ void KDebugLogImpl::Flush() {
+ /* ... */
+ }
+
+ void KDebugLogImpl::Save() {
+ /* ... */
+ }
+
+ void KDebugLogImpl::Restore() {
+ /* ... */
+ }
+
+#else
+
+ #error "Unknown Debug device!"
+
+#endif
+
+}
diff --git a/libmesosphere/source/kern_debug_log_impl.hpp b/libmesosphere/source/kern_debug_log_impl.hpp
index fb065c53..a34591db 100644
--- a/libmesosphere/source/kern_debug_log_impl.hpp
+++ b/libmesosphere/source/kern_debug_log_impl.hpp
@@ -21,6 +21,7 @@ namespace ams::kern {
class KDebugLogImpl {
public:
static NOINLINE bool Initialize();
+ static NOINLINE void PutStringBySemihosting(const char *s);
static NOINLINE void PutChar(char c);
static NOINLINE void Flush();
diff --git a/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp b/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp
new file mode 100644
index 00000000..578761d6
--- /dev/null
+++ b/libmesosphere/source/kern_k_memory_layout.board.qemu_virt.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::kern {
+
+ namespace {
+
+ constexpr uintptr_t DramPhysicalAddress = 0x40000000;
+ constexpr size_t ReservedEarlyDramSize = 0x00080000;
+
+ constexpr size_t CarveoutAlignment = 0x20000;
+ constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment;
+
+ template requires (std::same_as && ...)
+ constexpr ALWAYS_INLINE KMemoryRegionType GetMemoryRegionType(KMemoryRegionType base, T... attr) {
+ return util::FromUnderlying(util::ToUnderlying(base) | (util::ToUnderlying(attr) | ...));
+ }
+
+ void InsertPoolPartitionRegionIntoBothTrees(size_t start, size_t size, KMemoryRegionType phys_type, KMemoryRegionType virt_type, u32 &cur_attr) {
+ const u32 attr = cur_attr++;
+ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(start, size, phys_type, attr));
+ const KMemoryRegion *phys = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(phys_type, attr);
+ MESOSPHERE_INIT_ABORT_UNLESS(phys != nullptr);
+ MESOSPHERE_INIT_ABORT_UNLESS(phys->GetEndAddress() != 0);
+ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size, virt_type, attr));
+ }
+
+ }
+
+ namespace init {
+
+ void SetupDevicePhysicalMemoryRegions() {
+ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x08000000, 0x10000, GetMemoryRegionType(KMemoryRegionType_InterruptDistributor, KMemoryRegionAttr_ShouldKernelMap)));
+ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x08010000, 0x10000, GetMemoryRegionType(KMemoryRegionType_InterruptCpuInterface, KMemoryRegionAttr_ShouldKernelMap)));
+ }
+
+ void SetupDramPhysicalMemoryRegions() {
+ const size_t intended_memory_size = KSystemControl::Init::GetIntendedMemorySize();
+ const KPhysicalAddress physical_memory_base_address = KSystemControl::Init::GetKernelPhysicalBaseAddress(DramPhysicalAddress);
+
+ /* Insert blocks into the tree. */
+ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), intended_memory_size, KMemoryRegionType_Dram));
+ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly));
+
+ /* Insert the KTrace block at the end of Dram, if KTrace is enabled. */
+ static_assert(!IsKTraceEnabled || KTraceBufferSize > 0);
+ if constexpr (IsKTraceEnabled) {
+ const KPhysicalAddress ktrace_buffer_phys_addr = physical_memory_base_address + intended_memory_size - KTraceBufferSize;
+ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(ktrace_buffer_phys_addr), KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer));
+ }
+ }
+
+ void SetupPoolPartitionMemoryRegions() {
+ /* Start by identifying the extents of the DRAM memory region. */
+ const auto dram_extents = KMemoryLayout::GetMainMemoryPhysicalExtents();
+ MESOSPHERE_INIT_ABORT_UNLESS(dram_extents.GetEndAddress() != 0);
+
+ /* Determine the end of the pool region. */
+ const uintptr_t pool_end = dram_extents.GetEndAddress() - KTraceBufferSize;
+
+ /* Find the start of the kernel DRAM region. */
+ const KMemoryRegion *kernel_dram_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramKernelBase);
+ MESOSPHERE_INIT_ABORT_UNLESS(kernel_dram_region != nullptr);
+
+ const uintptr_t kernel_dram_start = kernel_dram_region->GetAddress();
+ MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(kernel_dram_start, CarveoutAlignment));
+
+ /* Find the start of the pool partitions region. */
+ const KMemoryRegion *pool_partitions_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(KMemoryRegionType_DramPoolPartition, 0);
+ MESOSPHERE_INIT_ABORT_UNLESS(pool_partitions_region != nullptr);
+ const uintptr_t pool_partitions_start = pool_partitions_region->GetAddress();
+
+ /* Setup the pool partition layouts. */
+ /* Get Application and Applet pool sizes. */
+ const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize();
+ const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize();
+ const size_t unsafe_system_pool_min_size = KSystemControl::Init::GetMinimumNonSecureSystemPoolSize();
+
+ /* Decide on starting addresses for our pools. */
+ const uintptr_t application_pool_start = pool_end - application_pool_size;
+ const uintptr_t applet_pool_start = application_pool_start - applet_pool_size;
+ const uintptr_t unsafe_system_pool_start = std::min(kernel_dram_start + CarveoutSizeMax, util::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment));
+ const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start;
+
+ /* We want to arrange application pool depending on where the middle of dram is. */
+ const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2;
+ u32 cur_pool_attr = 0;
+ size_t total_overhead_size = 0;
+ if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) {
+ InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
+ total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(application_pool_size);
+ } else {
+ const size_t first_application_pool_size = dram_midpoint - application_pool_start;
+ const size_t second_application_pool_size = application_pool_start + application_pool_size - dram_midpoint;
+ InsertPoolPartitionRegionIntoBothTrees(application_pool_start, first_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
+ InsertPoolPartitionRegionIntoBothTrees(dram_midpoint, second_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr);
+ total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size);
+ total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size);
+ }
+
+ /* Insert the applet pool. */
+ InsertPoolPartitionRegionIntoBothTrees(applet_pool_start, applet_pool_size, KMemoryRegionType_DramAppletPool, KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr);
+ total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size);
+
+ /* Insert the nonsecure system pool. */
+ InsertPoolPartitionRegionIntoBothTrees(unsafe_system_pool_start, unsafe_system_pool_size, KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, cur_pool_attr);
+ total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size);
+
+ /* Insert the pool management region. */
+ total_overhead_size += KMemoryManager::CalculateManagementOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size);
+ const uintptr_t pool_management_start = unsafe_system_pool_start - total_overhead_size;
+ const size_t pool_management_size = total_overhead_size;
+ u32 pool_management_attr = 0;
+ InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr);
+
+ /* Insert the system pool. */
+ const uintptr_t system_pool_size = pool_management_start - pool_partitions_start;
+ InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp b/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp
index 50bc3dce..1ffb3cda 100644
--- a/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp
+++ b/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp
@@ -26,6 +26,11 @@
return ::svcSetHeapSize(reinterpret_cast(out_address), size);
}
+ ALWAYS_INLINE Result SetHeapSize(uintptr_t *out_address, ::ams::svc::Size size) {
+ static_assert(sizeof(::ams::svc::Address) == sizeof(uintptr_t));
+ return ::svcSetHeapSize(reinterpret_cast(out_address), size);
+ }
+
ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) {
return ::svcSetMemoryPermission(reinterpret_cast(static_cast(address)), size, static_cast(perm));
}
diff --git a/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp b/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp
index 5f2d2128..4a36bb94 100644
--- a/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp
+++ b/libstratosphere/source/init/init_libnx_shim.os.horizon.cpp
@@ -38,6 +38,8 @@ namespace ams {
namespace init {
+ void InitializeSystemModuleBeforeConstructors();
+
void InitializeSystemModule();
void FinalizeSystemModule();
@@ -66,6 +68,9 @@ extern "C" void __libnx_initheap(void) {
extern "C" void __appInit(void) {
/* The very first thing all stratosphere code must do is initialize the os library. */
::ams::hos::InitializeForStratosphere();
+
+ /* Perform pre-C++ constructor init. */
+ ::ams::init::InitializeSystemModuleBeforeConstructors();
}
extern "C" void __appExit(void) {
diff --git a/libstratosphere/source/init/init_system_module.cpp b/libstratosphere/source/init/init_system_module.cpp
index 75ccce4a..0f64a107 100644
--- a/libstratosphere/source/init/init_system_module.cpp
+++ b/libstratosphere/source/init/init_system_module.cpp
@@ -17,6 +17,10 @@
namespace ams::init {
+ WEAK_SYMBOL void InitializeSystemModuleBeforeConstructors() {
+ /* This should only be used in exceptional circumstances. */
+ }
+
WEAK_SYMBOL void InitializeSystemModule() {
/* TODO: What should we do here, if anything? */
/* Nintendo does nndiagStartup(); nn::diag::InitializeSystemProcessAbortObserver(); */
diff --git a/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp b/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp
new file mode 100644
index 00000000..5160803a
--- /dev/null
+++ b/libvapours/include/vapours/svc/board/qemu/virt/svc_hardware_constants.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#pragma once
+#include
+
+namespace ams::svc::board::qemu::virt {
+
+ constexpr inline const s64 TicksPerSecond = 19'200'000;
+
+}
diff --git a/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp b/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp
index e02784a8..ccd6db31 100644
--- a/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp
+++ b/libvapours/include/vapours/svc/svc_select_hardware_constants.hpp
@@ -24,8 +24,15 @@
using namespace ams::svc::board::nintendo::nx;
}
+#elif defined(ATMOSPHERE_BOARD_QEMU_VIRT)
+
+ #include
+ namespace ams::svc {
+ using namespace ams::svc::board::qemu::virt;
+ }
+
#else
- #error "Unknown board for svc::DeviceName"
+ #error "Unknown board for svc Hardware Constants"
#endif