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
This commit is contained in:
fincs 2020-07-01 21:02:22 +02:00
parent 53cbfc231f
commit 4e387679f7
No known key found for this signature in database
GPG Key ID: 62C7609ADA219C60
3 changed files with 128 additions and 53 deletions

View File

@ -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;
}
/**

View File

@ -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)

View File

@ -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 <sys/time.h>
@ -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);
}
//---------------------------------------------------------------------------------