From 4e387679f77715701d1f3c4074e8f1dfac09f13d Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 1 Jul 2020 21:02:22 +0200 Subject: [PATCH] Add structs for kernel/shared pages + related refactor, see details: - Added osKernelConfig_s/osSharedConfig_s structs - Added OS_KernelConfig/OS_SharedConfig macros for accessing the pages - Replaced 0x1FF8nnnn hardcoded addresses with struct accesses - Added osIsHeadsetConnected function - Refactored and rewritten RTC time support with the correct algorithm (many thanks to @TuxSH for reverse engineering the PTM sysmodule): - Fully added and documented the osTimeRef_s structure - This replaces the previous internal datetime_t struct type - Added osGetTimeRef function: - This replaces the previous internal getSysTime function - Added missing barrier to the lock free reading algorithm - osGetTime rewritten to implement the drift-correction algorithm - **This introduces a dependency to __aeabi_ldivmod** - __libctru_gtod rewritten to reuse osGetTime --- libctru/include/3ds/os.h | 92 +++++++++++++++++++++++++++++++++++--- libctru/source/ndsp/ndsp.c | 2 +- libctru/source/os.c | 87 +++++++++++++++++------------------ 3 files changed, 128 insertions(+), 53 deletions(-) diff --git a/libctru/include/3ds/os.h b/libctru/include/3ds/os.h index e94feb1..9829097 100644 --- a/libctru/include/3ds/os.h +++ b/libctru/include/3ds/os.h @@ -52,10 +52,73 @@ #define OS_DSPRAM_PADDR 0x1FF00000 ///< DSP memory physical address #define OS_DSPRAM_SIZE 0x80000 ///< DSP memory size (512 KiB) +#define OS_KERNELCFG_VADDR 0x1FF80000 ///< Kernel configuration page virtual address +#define OS_SHAREDCFG_VADDR 0x1FF81000 ///< Shared system configuration page virtual address + #define OS_FCRAM_VADDR 0x30000000 ///< Linear FCRAM mapping virtual address #define OS_FCRAM_PADDR 0x20000000 ///< Linear FCRAM mapping physical address #define OS_FCRAM_SIZE 0x10000000 ///< Linear FCRAM mapping size (256 MiB) +#define OS_KernelConfig ((osKernelConfig_s const*)OS_KERNELCFG_VADDR) ///< Pointer to the kernel configuration page (see \ref osKernelConfig_s) +#define OS_SharedConfig ((osSharedConfig_s*)OS_SHAREDCFG_VADDR) ///< Pointer to the shared system configuration page (see \ref osSharedConfig_s) + +/// Kernel configuration page (read-only). +typedef struct +{ + u32 kernel_ver; + u32 update_flag; + u64 ns_tid; + u32 kernel_syscore_ver; + u8 env_info; + u8 unit_info; + u8 boot_env; + u8 unk_0x17; + u32 kernel_ctrsdk_ver; + u32 unk_0x1c; + u32 firmlaunch_flags; + u8 unk_0x24[0xc]; + u32 app_memtype; + u8 unk_0x34[0x10]; + u32 memregion_sz[3]; + u8 unk_0x4c[0x14]; + u32 firm_ver; + u32 firm_syscore_ver; + u32 firm_ctrsdk_ver; +} osKernelConfig_s; + +/// Time reference information struct (filled in by PTM). +typedef struct +{ + u64 value_ms; ///< Milliseconds elapsed since January 1900 when this structure was last updated + u64 value_tick; ///< System ticks elapsed since boot when this structure was last updated + s64 sysclock_hz;///< System clock frequency in Hz adjusted using RTC measurements (usually around \ref SYSCLOCK_ARM11) + s64 drift_ms; ///< Measured time drift of the system clock (according to the RTC) in milliseconds since the last update +} osTimeRef_s; + +/// Shared system configuration page structure (read-only or read-write depending on exheader). +typedef struct +{ + vu32 timeref_cnt; + u8 running_hw; + u8 mcu_hwinfo; + u8 unk_0x06[0x1A]; + volatile osTimeRef_s timeref[2]; + u8 wifi_macaddr[6]; + vu8 wifi_strength; + vu8 network_state; + u8 unk_0x68[0x18]; + volatile float slider_3d; + vu8 led_3d; + vu8 led_battery; + vu8 unk_flag; + u8 unk_0x87; + u8 unk_0x88[0x18]; + vu64 menu_tid; + vu64 cur_menu_tid; + u8 unk_0xB0[0x10]; + vu8 headset_connected; +} osSharedConfig_s; + /// Tick counter. typedef struct { @@ -106,7 +169,7 @@ const char* osStrError(Result error); */ static inline u32 osGetFirmVersion(void) { - return (*(vu32*)0x1FF80060) & ~0xFF; + return OS_KernelConfig->firm_ver &~ 0xFF; } /** @@ -121,19 +184,19 @@ static inline u32 osGetFirmVersion(void) */ static inline u32 osGetKernelVersion(void) { - return (*(vu32*)0x1FF80000) & ~0xFF; + return OS_KernelConfig->kernel_ver &~ 0xFF; } /// Gets the system's "core version" (2 on NATIVE_FIRM, 3 on SAFE_FIRM, etc.) static inline u32 osGetSystemCoreVersion(void) { - return *(vu32*)0x1FF80010; + return OS_KernelConfig->kernel_syscore_ver; } /// Gets the system's memory layout ID (0-5 on Old 3DS, 6-8 on New 3DS) static inline u32 osGetApplicationMemType(void) { - return *(vu32*)0x1FF80030; + return OS_KernelConfig->app_memtype; } /** @@ -146,7 +209,7 @@ static inline u32 osGetMemRegionSize(MemRegion region) if(region == MEMREGION_ALL) { return osGetMemRegionSize(MEMREGION_APPLICATION) + osGetMemRegionSize(MEMREGION_SYSTEM) + osGetMemRegionSize(MEMREGION_BASE); } else { - return *(vu32*) (0x1FF80040 + (region - 1) * 0x4); + return OS_KernelConfig->memregion_sz[region-1]; } } @@ -172,6 +235,12 @@ static inline u32 osGetMemRegionFree(MemRegion region) return osGetMemRegionSize(region) - osGetMemRegionUsed(region); } +/** + * @brief Reads the latest reference timepoint published by PTM. + * @return Structure (see \ref osTimeRef_s). + */ +osTimeRef_s osGetTimeRef(void); + /** * @brief Gets the current time. * @return The number of milliseconds since 1st Jan 1900 00:00. @@ -222,7 +291,7 @@ double osTickCounterRead(const TickCounter* cnt); */ static inline u8 osGetWifiStrength(void) { - return *(vu8*)0x1FF81066; + return OS_SharedConfig->wifi_strength; } /** @@ -231,7 +300,16 @@ static inline u8 osGetWifiStrength(void) */ static inline float osGet3DSliderState(void) { - return *(volatile float*)0x1FF81080; + return OS_SharedConfig->slider_3d; +} + +/** + * @brief Checks whether a headset is connected. + * @return true or false. + */ +static inline bool osIsHeadsetConnected(void) +{ + return OS_SharedConfig->headset_connected != 0; } /** diff --git a/libctru/source/ndsp/ndsp.c b/libctru/source/ndsp/ndsp.c index 41f682b..e868bc0 100644 --- a/libctru/source/ndsp/ndsp.c +++ b/libctru/source/ndsp/ndsp.c @@ -142,7 +142,7 @@ static void ndspUpdateMaster(void) u32 flags = m->flags, mflags = ndspMaster.flags; int i; - m->headsetConnected = *(vu8*)0x1FF810C0; + m->headsetConnected = osIsHeadsetConnected(); flags |= 0x10000000; if (mflags & MFLAG_MASTERVOL) diff --git a/libctru/source/os.c b/libctru/source/os.c index 3b95a17..8a65daa 100644 --- a/libctru/source/os.c +++ b/libctru/source/os.c @@ -2,6 +2,7 @@ #include <3ds/result.h> #include <3ds/os.h> #include <3ds/svc.h> +#include <3ds/synchronization.h> #include <3ds/services/ptmsysm.h> #include @@ -13,16 +14,6 @@ static inline double u64_to_double(u64 value) { return (((double)(u32)(value >> 32))*0x100000000ULL+(u32)value); } -typedef struct { - u64 date_time; - u64 update_tick; - //... -} datetime_t; - -#define __datetime_selector (*(vu32*)0x1FF81000) -#define __datetime0 (*(volatile datetime_t*)0x1FF81020) -#define __datetime1 (*(volatile datetime_t*)0x1FF81040) - __attribute__((weak)) bool __ctru_speedup = false; //--------------------------------------------------------------------------------- @@ -56,60 +47,66 @@ void* osConvertOldLINEARMemToNew(const void* addr) { } //--------------------------------------------------------------------------------- -static datetime_t getSysTime(void) { +osTimeRef_s osGetTimeRef(void) { //--------------------------------------------------------------------------------- - u32 s1, s2 = __datetime_selector & 1; - datetime_t dt; + osTimeRef_s tr; + u32 next = OS_SharedConfig->timeref_cnt; + u32 cur; do { - s1 = s2; - if(!s1) - dt = __datetime0; - else - dt = __datetime1; - s2 = __datetime_selector & 1; - } while(s2 != s1); + cur = next; + tr = OS_SharedConfig->timeref[cur&1]; + __dmb(); + next = OS_SharedConfig->timeref_cnt; + } while (cur != next); - return dt; + return tr; +} + +//--------------------------------------------------------------------------------- +u64 osGetTime(void) { +//--------------------------------------------------------------------------------- + // Read the latest time reference point published by PTM + osTimeRef_s tr = osGetTimeRef(); + + // Calculate the time elapsed since the reference point using the system clock + s64 elapsed_tick = svcGetSystemTick() - tr.value_tick; + s64 elapsed_ms = elapsed_tick * 1000 / tr.sysclock_hz; + + // Apply the drift adjustment if present: + // Every time PTM publishes a new reference point it measures by how long the + // system clock has drifted with respect to the RTC. It also recalculates the + // system clock frequency using RTC data in order to minimize future drift. + // The idea behind the following logic is to reapply the inaccuracy to the + // calculated timestamp, but gradually reducing it to zero over the course of + // an hour. This ensures the monotonic growth of the returned time value. + const s64 hour_in_ms = 60*60*1000; // milliseconds in an hour + if (tr.drift_ms != 0 && elapsed_ms < hour_in_ms) + elapsed_ms += tr.drift_ms * (hour_in_ms - elapsed_ms) / hour_in_ms; + + // Return the final timestamp + return tr.value_ms + elapsed_ms; } //--------------------------------------------------------------------------------- int __libctru_gtod(struct _reent *ptr, struct timeval *tp, struct timezone *tz) { //--------------------------------------------------------------------------------- if (tp != NULL) { + // Retrieve current time, adjusting epoch from 1900 to 1970 + s64 now = osGetTime() - 2208988800000ULL; - datetime_t dt = getSysTime(); - - u64 delta = svcGetSystemTick() - dt.update_tick; - - u32 offset = (u32)(u64_to_double(delta)/CPU_TICKS_PER_USEC); - - // adjust from 1900 to 1970 - u64 now = ((dt.date_time - 2208988800000ULL) * 1000) + offset; - - tp->tv_sec = u64_to_double(now)/1000000.0; - tp->tv_usec = now - ((tp->tv_sec) * 1000000); - + // Convert to struct timeval + tp->tv_sec = now / 1000; + tp->tv_usec = (now - 1000*tp->tv_sec) * 1000; } if (tz != NULL) { + // Provide dummy information, as the 3DS does not have the concept of timezones tz->tz_minuteswest = 0; tz->tz_dsttime = 0; } return 0; - -} - -// Returns number of milliseconds since 1st Jan 1900 00:00. -//--------------------------------------------------------------------------------- -u64 osGetTime(void) { -//--------------------------------------------------------------------------------- - datetime_t dt = getSysTime(); - - u64 delta = svcGetSystemTick() - dt.update_tick; - - return dt.date_time + (u32)(u64_to_double(delta)/CPU_TICKS_PER_MSEC); } //---------------------------------------------------------------------------------