diff --git a/nx/include/switch/kernel/detect.h b/nx/include/switch/kernel/detect.h index 95ca01be..49125e1e 100644 --- a/nx/include/switch/kernel/detect.h +++ b/nx/include/switch/kernel/detect.h @@ -6,9 +6,14 @@ */ #pragma once #include "../types.h" +#include "../runtime/hosversion.h" + +/// Returns the lowest known kernel version that is compatible with observation. +u32 detectKernelVersion(void); + +/// Returns the highest possible known kernel version that is compatible with observation. +u32 detectKernelVersionUpperBound(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. @@ -18,25 +23,25 @@ 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; + return detectKernelVersion() >= MAKEHOSVERSION(2,0,0); } /// 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; + return detectKernelVersion() >= MAKEHOSVERSION(3,0,0); } /// 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; + return detectKernelVersion() >= MAKEHOSVERSION(4,0,0); } /// 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; + return detectKernelVersion() >= MAKEHOSVERSION(5,0,0); } /// 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; + return detectKernelVersion() >= MAKEHOSVERSION(6,0,0); } diff --git a/nx/include/switch/runtime/hosversion.h b/nx/include/switch/runtime/hosversion.h index f426ade8..1f88a766 100644 --- a/nx/include/switch/runtime/hosversion.h +++ b/nx/include/switch/runtime/hosversion.h @@ -19,24 +19,18 @@ /// Extracts the micro number from a HOS version value. #define HOSVER_MICRO(_version) ( (_version) & 0xFF) -/// Returns the current HOS version. Normally, this matches the version returned by \ref setsysGetFirmwareVersion or, if set:sys is not available, the version detected by \ref detectKernelVersion. -u32 hosversionGet(void); +void hosversionSetup(void); /// Sets or overrides the current HOS version. This function is normally called automatically by libnx on startup. 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); -} +bool hosversionAtLeast(u8 major, u8 minor, u8 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); -} +bool hosversionBefore(u8 major, u8 minor, u8 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); + return hosversionAtLeast(major1, 0, 0) && hosversionBefore(major2, 0, 0); } diff --git a/nx/source/applets/swkbd.c b/nx/source/applets/swkbd.c index 7088e82f..65f3d98c 100644 --- a/nx/source/applets/swkbd.c +++ b/nx/source/applets/swkbd.c @@ -51,14 +51,13 @@ static void _swkbdConfigClear(SwkbdConfig* c) { } static void _swkbdInitVersion(u32* version) { - u32 hosver = hosversionGet(); - if (hosver >= MAKEHOSVERSION(5,0,0)) + if (hosversionAtLeast(5,0,0)) *version = 0x50009; - else if (hosver >= MAKEHOSVERSION(4,0,0)) + else if (hosversionAtLeast(4,0,0)) *version = 0x40008; - else if (hosver >= MAKEHOSVERSION(3,0,0)) + else if (hosversionAtLeast(3,0,0)) *version = 0x30007; - else if (hosver >= MAKEHOSVERSION(2,0,0)) + else if (hosversionAtLeast(2,0,0)) *version = 0x10006; else *version=0x5;//1.0.0+ version diff --git a/nx/source/kernel/detect.c b/nx/source/kernel/detect.c index f8a6b4fa..46dc12ea 100644 --- a/nx/source/kernel/detect.c +++ b/nx/source/kernel/detect.c @@ -4,12 +4,23 @@ #include "kernel/detect.h" #include "kernel/mutex.h" #include "kernel/svc.h" +#include "runtime/hosversion.h" #include static bool g_VersionCached; static Mutex g_VersionMutex; static int g_Version; +static u32 g_VersionBounds[] = { + MAKEHOSVERSION(1,0,0), + MAKEHOSVERSION(2,0,0), + MAKEHOSVERSION(3,0,0), + MAKEHOSVERSION(4,0,0), + MAKEHOSVERSION(5,0,0), + MAKEHOSVERSION(6,0,0), + UINT32_MAX +}; + static bool g_JitKernelPatchCached; static Mutex g_JitKernelPatchMutex; static bool g_JitKernelPatchDetected; @@ -27,17 +38,17 @@ static void _CacheVersion(void) } u64 tmp; - g_Version = 1; + g_Version = 0; if (R_VALUE(svcGetInfo(&tmp, 12, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) - g_Version = 2; + g_Version = 1; if (R_VALUE(svcGetInfo(&tmp, 18, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) - g_Version = 3; + g_Version = 2; if (R_VALUE(svcGetInfo(&tmp, 19, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) - g_Version = 4; + g_Version = 3; if (R_VALUE(svcGetInfo(&tmp, 20, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) - g_Version = 5; + g_Version = 4; if (R_VALUE(svcGetInfo(&tmp, 21, INVALID_HANDLE, 0)) != KERNELRESULT(InvalidEnumValue)) - g_Version = 6; + g_Version = 5; __atomic_store_n(&g_VersionCached, true, __ATOMIC_SEQ_CST); @@ -81,9 +92,14 @@ static void _CacheJitKernelPatch(void) mutexUnlock(&g_JitKernelPatchMutex); } -int detectKernelVersion(void) { +u32 detectKernelVersion(void) { _CacheVersion(); - return g_Version; + return g_VersionBounds[g_Version]; +} + +u32 detectKernelVersionUpperBound(void) { + _CacheVersion(); + return g_VersionBounds[g_Version + 1]; } bool detectDebugger(void) { diff --git a/nx/source/runtime/hosversion.c b/nx/source/runtime/hosversion.c index 5bffb725..a5d7855b 100644 --- a/nx/source/runtime/hosversion.c +++ b/nx/source/runtime/hosversion.c @@ -1,13 +1,47 @@ #include "runtime/hosversion.h" +#include "kernel/detect.h" +#include "services/fatal.h" + +static u32 g_kernelLowerBound; +static u32 g_kernelUpperBound; static u32 g_hosVersion; +static bool g_hasHosVersion; -u32 hosversionGet(void) +void hosversionSetup(void) { - return __atomic_load_n(&g_hosVersion, __ATOMIC_SEQ_CST); + g_kernelLowerBound = detectKernelVersion(); + g_kernelUpperBound = detectKernelVersionUpperBound(); } void hosversionSet(u32 version) { - __atomic_store_n(&g_hosVersion, version, __ATOMIC_SEQ_CST); + g_hosVersion = version; + g_hasHosVersion = true; +} + +bool hosversionAtLeast(u8 major, u8 minor, u8 micro) +{ + u32 ver = MAKEHOSVERSION(major, minor, micro); + + if (g_hasHosVersion) + return g_hosVersion >= ver; + + if (g_kernelLowerBound >= ver) + return true; + + fatalSimple(-1); +} + +bool hosversionBefore(u8 major, u8 minor, u8 micro) +{ + u32 ver = MAKEHOSVERSION(major, minor, micro); + + if (g_hasHosVersion) + return g_hosVersion < ver; + + if (g_kernelUpperBound < ver) + return true; + + fatalSimple(-1); } diff --git a/nx/source/runtime/init.c b/nx/source/runtime/init.c index b8cb9cc7..276ca260 100644 --- a/nx/source/runtime/init.c +++ b/nx/source/runtime/init.c @@ -168,7 +168,7 @@ void __attribute__((weak)) __libnx_init(void* ctx, Handle main_thread, void* sav // Libnx initialization goes here. envSetup(ctx, main_thread, saved_lr); - hosversionSet(MAKEHOSVERSION(detectKernelVersion(), 0, 0)); + hosversionSetup(); newlibSetup(); virtmemSetup(); __libnx_initheap(); diff --git a/nx/source/services/audren.c b/nx/source/services/audren.c index a03f8124..9f286912 100644 --- a/nx/source/services/audren.c +++ b/nx/source/services/audren.c @@ -56,16 +56,15 @@ Result audrenInitialize(const AudioRendererConfig* config) return MAKERESULT(Module_Libnx, LibnxError_BadInput); // Choose revision (i.e. if splitters are used then at least revision 2 must be used) - u32 hosver = hosversionGet(); /*if (hosver >= MAKEHOSVERSION(6,1,0)) g_audrenRevision = AUDREN_REVISION_6; else if (hosver >= MAKEHOSVERSION(6,0,0)) g_audrenRevision = AUDREN_REVISION_5; - else*/ if (hosver >= MAKEHOSVERSION(4,0,0)) + else*/ if (hosversionAtLeast(4,0,0)) g_audrenRevision = AUDREN_REVISION_4; - else if (hosver >= MAKEHOSVERSION(3,0,0)) + else if (hosversionAtLeast(3,0,0)) g_audrenRevision = AUDREN_REVISION_3; - else if (hosver >= MAKEHOSVERSION(2,0,0)) + else if (hosversionAtLeast(2,0,0)) g_audrenRevision = AUDREN_REVISION_2; else g_audrenRevision = AUDREN_REVISION_1;