diff --git a/nx/include/switch.h b/nx/include/switch.h index ba57091b..2521a092 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -108,6 +108,7 @@ extern "C" { #include "switch/applets/web.h" #include "switch/runtime/env.h" +#include "switch/runtime/hosversion.h" #include "switch/runtime/nxlink.h" #include "switch/runtime/util/utf.h" diff --git a/nx/include/switch/kernel/detect.h b/nx/include/switch/kernel/detect.h index 1871b872..95ca01be 100644 --- a/nx/include/switch/kernel/detect.h +++ b/nx/include/switch/kernel/detect.h @@ -1,26 +1,42 @@ /** * @file detect.h - * @brief Kernel version detection + * @brief Kernel capability detection * @author plutoo * @copyright libnx Authors */ #pragma once #include "../types.h" -/// Returns true if the kernel version is equal to or above 2.0.0. -bool kernelAbove200(void); -/// Returns true if the kernel version is equal to or above 3.0.0. -bool kernelAbove300(void); -/// Returns true if the kernel version is equal to or above 4.0.0. -bool kernelAbove400(void); -/// Returns true if the kernel version is equal to or above 5.0.0. -bool kernelAbove500(void); -/// Returns true if the kernel version is equal to or above 6.0.0. -bool kernelAbove600(void); +/// Returns the kernel version that can be detected by checking kernel capabilities. This only goes from 1 (representing 1.0.0) up to 6 (representing 6.0.0 and above). Generally, \ref hosversionGet should be used instead of this function. +int detectKernelVersion(void); /// Returns true if the process has a debugger attached. bool detectDebugger(void); - /// Returns true if the kernel is patched to allow self-process-jit. bool detectJitKernelPatch(void); /// After this has been called, libnx will ignore the self-process-jit kernel patch. For testing purposes only. void detectIgnoreJitKernelPatch(void); + +/// Returns true if the kernel version is equal to or above 2.0.0. Generally, \ref hosversionAtLeast should be used instead of this function. +static inline bool kernelAbove200(void) { + return detectKernelVersion() >= 2; +} + +/// Returns true if the kernel version is equal to or above 3.0.0. Generally, \ref hosversionAtLeast should be used instead of this function. +static inline bool kernelAbove300(void) { + return detectKernelVersion() >= 3; +} + +/// Returns true if the kernel version is equal to or above 4.0.0. Generally, \ref hosversionAtLeast should be used instead of this function. +static inline bool kernelAbove400(void) { + return detectKernelVersion() >= 4; +} + +/// Returns true if the kernel version is equal to or above 5.0.0. Generally, \ref hosversionAtLeast should be used instead of this function. +static inline bool kernelAbove500(void) { + return detectKernelVersion() >= 5; +} + +/// Returns true if the kernel version is equal to or above 6.0.0. Generally, \ref hosversionAtLeast should be used instead of this function. +static inline bool kernelAbove600(void) { + return detectKernelVersion() >= 6; +} diff --git a/nx/include/switch/runtime/hosversion.h b/nx/include/switch/runtime/hosversion.h new file mode 100644 index 00000000..d03c642e --- /dev/null +++ b/nx/include/switch/runtime/hosversion.h @@ -0,0 +1,42 @@ +/** + * @file hosversion.h + * @brief Horizon OS (HOS) version detection utilities. + * @author fincs + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" + +/// Builds a HOS version value from its constituent components. +#define MAKEHOSVERSION(_major,_minor,_micro) (((u32)(_major) << 16) | ((u32)(_minor) << 8) | (u32)(_micro)) + +/// Extracts the major number from a HOS version value. +#define HOSVER_MAJOR(_version) (((_version) >> 16) & 0xFF) + +/// Extracts the minor number from a HOS version value. +#define HOSVER_MINOR(_version) (((_version) >> 8) & 0xFF) + +/// Extracts the micro number from a HOS version value. +#define HOSVER_MICRO(_version) ( (_version) & 0xFF) + +/// Returns the current HOS version that was previously set with \ref hosversionSet. If version initialization fails during startup (such as in the case set:sys is not available), this function returns zero. +u32 hosversionGet(void); + +/// Sets or overrides the current HOS version. This function is normally called automatically by libnx on startup with the version info obtained with \ref setsysGetFirmwareVersion. +void hosversionSet(u32 version); + +/// Returns true if the current HOS version is equal to or above the specified major/minor/micro version. +static inline bool hosversionAtLeast(u8 major, u8 minor, u8 micro) { + return hosversionGet() >= MAKEHOSVERSION(major,minor,micro); +} + +/// Returns true if the current HOS version is earlier than the specified major/minor/micro version. +static inline bool hosversionBefore(u8 major, u8 minor, u8 micro) { + return !hosversionAtLeast(major, minor, micro); +} + +/// Returns true if the current HOS version is between the two specified major versions, i.e. [major1, major2). +static inline bool hosversionBetween(u8 major1, u8 major2) { + u32 ver = hosversionGet(); + return ver >= MAKEHOSVERSION(major1,0,0) && ver < MAKEHOSVERSION(major2,0,0); +} diff --git a/nx/source/kernel/detect.c b/nx/source/kernel/detect.c index a5d44dbf..f8a6b4fa 100644 --- a/nx/source/kernel/detect.c +++ b/nx/source/kernel/detect.c @@ -6,15 +6,11 @@ #include "kernel/svc.h" #include -static bool g_VersionCached = 0; +static bool g_VersionCached; static Mutex g_VersionMutex; -static bool g_IsAbove200; -static bool g_IsAbove300; -static bool g_IsAbove400; -static bool g_IsAbove500; -static bool g_IsAbove600; +static int g_Version; -static bool g_JitKernelPatchCached = 0; +static bool g_JitKernelPatchCached; static Mutex g_JitKernelPatchMutex; static bool g_JitKernelPatchDetected; @@ -31,16 +27,17 @@ static void _CacheVersion(void) } u64 tmp; - g_IsAbove200 = (svcGetInfo(&tmp, 12, INVALID_HANDLE, 0) != 0xF001); - g_IsAbove300 = (svcGetInfo(&tmp, 18, INVALID_HANDLE, 0) != 0xF001); - g_IsAbove400 = (svcGetInfo(&tmp, 19, INVALID_HANDLE, 0) != 0xF001); - g_IsAbove500 = (svcGetInfo(&tmp, 20, INVALID_HANDLE, 0) != 0xF001); - g_IsAbove600 = (svcGetInfo(&tmp, 21, INVALID_HANDLE, 0) != 0xF001); - - g_IsAbove500 |= g_IsAbove600; - g_IsAbove400 |= g_IsAbove500; - g_IsAbove300 |= g_IsAbove400; - g_IsAbove200 |= g_IsAbove300; + g_Version = 1; + if (R_VALUE(svcGetInfo(&tmp, 12, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) + g_Version = 2; + if (R_VALUE(svcGetInfo(&tmp, 18, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) + g_Version = 3; + if (R_VALUE(svcGetInfo(&tmp, 19, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) + g_Version = 4; + if (R_VALUE(svcGetInfo(&tmp, 20, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) + g_Version = 5; + if (R_VALUE(svcGetInfo(&tmp, 21, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) + g_Version = 6; __atomic_store_n(&g_VersionCached, true, __ATOMIC_SEQ_CST); @@ -84,29 +81,9 @@ static void _CacheJitKernelPatch(void) mutexUnlock(&g_JitKernelPatchMutex); } -bool kernelAbove200(void) { +int detectKernelVersion(void) { _CacheVersion(); - return g_IsAbove200; -} - -bool kernelAbove300(void) { - _CacheVersion(); - return g_IsAbove300; -} - -bool kernelAbove400(void) { - _CacheVersion(); - return g_IsAbove400; -} - -bool kernelAbove500(void) { - _CacheVersion(); - return g_IsAbove500; -} - -bool kernelAbove600(void) { - _CacheVersion(); - return g_IsAbove600; + return g_Version; } bool detectDebugger(void) { diff --git a/nx/source/runtime/hosversion.c b/nx/source/runtime/hosversion.c new file mode 100644 index 00000000..5bffb725 --- /dev/null +++ b/nx/source/runtime/hosversion.c @@ -0,0 +1,13 @@ +#include "runtime/hosversion.h" + +static u32 g_hosVersion; + +u32 hosversionGet(void) +{ + return __atomic_load_n(&g_hosVersion, __ATOMIC_SEQ_CST); +} + +void hosversionSet(u32 version) +{ + __atomic_store_n(&g_hosVersion, version, __ATOMIC_SEQ_CST); +} diff --git a/nx/source/runtime/init.c b/nx/source/runtime/init.c index fc7b5c23..8a9ab187 100644 --- a/nx/source/runtime/init.c +++ b/nx/source/runtime/init.c @@ -1,11 +1,13 @@ #include "types.h" #include "runtime/env.h" +#include "runtime/hosversion.h" #include "services/sm.h" #include "services/fatal.h" #include "services/fs.h" #include "services/hid.h" #include "services/time.h" #include "services/applet.h" +#include "services/set.h" #include "runtime/devices/fs_dev.h" void* __stack_top; @@ -107,6 +109,15 @@ void __attribute__((weak)) __appInit(void) if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); + rc = setsysInitialize(); + if (R_SUCCEEDED(rc)) { + SetSysFirmwareVersion fw; + rc = setsysGetFirmwareVersion(&fw); + if (R_SUCCEEDED(rc)) + hosversionSet(MAKEHOSVERSION(fw.major, fw.minor, fw.micro)); + setsysExit(); + } + rc = appletInitialize(); if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_InitFail_AM));