From 25a57d7f382c919a149499439cb4d8bd07660a77 Mon Sep 17 00:00:00 2001 From: fincs Date: Sun, 17 Feb 2019 14:52:00 +0100 Subject: [PATCH] Introduce hosversion.h - HOS version detection utilities - Simplify kernel/detect.h logic; introduce detectKernelVersion() - Use the detected kernel version during early startup as a first approximation of the current HOS version (which only goes up to 6.0.0) - Use set:sys (if available) during __appInit in order to refine it to the actual current HOS version --- nx/include/switch.h | 1 + nx/include/switch/kernel/detect.h | 40 +++++++++++++------ nx/include/switch/runtime/hosversion.h | 42 ++++++++++++++++++++ nx/source/kernel/detect.c | 55 ++++++++------------------ nx/source/runtime/hosversion.c | 13 ++++++ nx/source/runtime/init.c | 13 ++++++ 6 files changed, 113 insertions(+), 51 deletions(-) create mode 100644 nx/include/switch/runtime/hosversion.h create mode 100644 nx/source/runtime/hosversion.c 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..f426ade8 --- /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. 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); + +/// 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); +} + +/// 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..b8cb9cc7 100644 --- a/nx/source/runtime/init.c +++ b/nx/source/runtime/init.c @@ -1,11 +1,14 @@ #include "types.h" +#include "kernel/detect.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 +110,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)); @@ -156,6 +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)); newlibSetup(); virtmemSetup(); __libnx_initheap();