Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions src/common/time.hpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,58 @@
#pragma once

// TODO: support cross-platform time functions

#include <chrono>
#include <mach/mach_time.h>

#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
#include <x86intrin.h>
#endif

#include "common/types.hpp"
#include "core/hw/tegra_x1/cpu/const.hpp"

using namespace std::chrono_literals;

namespace hydra {

inline u64 get_absolute_time() { return mach_absolute_time(); }
#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)

inline u64 GetSystemTick() {
_mm_lfence();
u64 res = __rdtsc();
_mm_lfence();
return res;
}

inline u64 GetSystemFrequency() {
auto nsc_start = std::chrono::steady_clock::now().time_since_epoch();
u64 tsc_start = GetSystemTick();
// More sleep, more precision.
std::this_thread::sleep_for(10ms);
Comment thread
SamoZ256 marked this conversation as resolved.
auto nsc_end = std::chrono::steady_clock::now().time_since_epoch();
u64 tsc_end = GetSystemTick();
u64 ns_diff =
static_cast<u64>(std::chrono::duration_cast<std::chrono::nanoseconds>(
nsc_end - nsc_start)
.count());
u64 res = (tsc_end - tsc_start) * 1000000000ULL / (ns_diff);
res = res + 100'000 / 2;
res -= res % 100'000;
return res;
}

#elif defined(_M_ARM64) || defined(__aarch64__)

inline u64 GetSystemTick() {
u64 res;
__asm__ __volatile__("mrs %0, cntvct_el0; " : "=r"(res)::"memory");
return res;
}

inline u64 GetSystemFrequency() {
u64 res;
__asm__ __volatile__("mrs %0, cntfrq_el0; isb; " : "=r"(res)::"memory");
return res;
}

#endif

} // namespace hydra
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ add_library(hydra-core
audio/null/stream.hpp
audio/null/core.cpp
audio/null/core.hpp
hw/wall_clock.cpp
hw/wall_clock.hpp
hw/generic_mmu.hpp
hw/tegra_x1/cpu/const.hpp
hw/tegra_x1/cpu/memory.hpp
Expand Down
11 changes: 7 additions & 4 deletions src/core/emulation_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ void EmulationContext::LoadAndStart(horizon::loader::LoaderBase* loader) {
// Check for firmware applets
auto controller = new horizon::services::am::LibraryAppletController(
horizon::LibraryAppletMode::AllForeground);
// TODO: correct?
u64 system_tick;
os->GetKernel().GetSystemTick(system_tick);
switch (loader->GetTitleID()) {
case 0x0100000000001003: { // controller
// Common args
Expand All @@ -138,7 +141,7 @@ void EmulationContext::LoadAndStart(horizon::loader::LoaderBase* loader) {
.library_applet_api_version = 1, // TODO: correct?
.theme_color = 0, // HACK
.play_startup_sound = false, // HACK
.system_tick = get_absolute_time(),
.system_tick = system_tick,
};
controller->PushInData(
new horizon::services::am::IStorage(common_args));
Expand Down Expand Up @@ -184,7 +187,7 @@ void EmulationContext::LoadAndStart(horizon::loader::LoaderBase* loader) {
.library_applet_api_version = 1, // TODO: correct?
.theme_color = 0, // HACK
.play_startup_sound = false, // HACK
.system_tick = get_absolute_time(),
.system_tick = system_tick,
};
controller->PushInData(
new horizon::services::am::IStorage(common_args));
Expand Down Expand Up @@ -219,7 +222,7 @@ void EmulationContext::LoadAndStart(horizon::loader::LoaderBase* loader) {
.library_applet_api_version = 1, // TODO: correct?
.theme_color = 0, // HACK
.play_startup_sound = false, // HACK
.system_tick = get_absolute_time(),
.system_tick = system_tick,
};
controller->PushInData(
new horizon::services::am::IStorage(common_args));
Expand Down Expand Up @@ -252,7 +255,7 @@ void EmulationContext::LoadAndStart(horizon::loader::LoaderBase* loader) {
.library_applet_api_version = 1, // TODO: correct?
.theme_color = 0, // HACK
.play_startup_sound = false, // HACK
.system_tick = get_absolute_time(),
.system_tick = system_tick,
};
controller->PushInData(
new horizon::services::am::IStorage(common_args));
Expand Down
2 changes: 2 additions & 0 deletions src/core/emulation_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "core/horizon/os.hpp"
#include "core/hw/tegra_x1/cpu/cpu.hpp"
#include "core/hw/tegra_x1/gpu/gpu.hpp"
#include "core/hw/wall_clock.hpp"

namespace hydra {

Expand Down Expand Up @@ -44,6 +45,7 @@ class EmulationContext {

private:
// Objects
hw::WallClock wall_clock;
hw::tegra_x1::cpu::ICpu* cpu;
hw::tegra_x1::gpu::Gpu* gpu;
audio::ICore* audio_core;
Expand Down
3 changes: 2 additions & 1 deletion src/core/horizon/kernel/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "core/hw/tegra_x1/cpu/cpu.hpp"
#include "core/hw/tegra_x1/cpu/mmu.hpp"
#include "core/hw/tegra_x1/cpu/thread.hpp"
#include "core/hw/wall_clock.hpp"

namespace hydra::horizon::kernel {

Expand Down Expand Up @@ -855,7 +856,7 @@ result_t Kernel::SignalProcessWideKey(Process* crnt_process, uptr addr,
void Kernel::GetSystemTick(u64& out_tick) {
LOG_DEBUG(Kernel, "GetSystemTick called");

out_tick = get_absolute_time();
out_tick = hw::WallClock::GetInstance().GetCntpct(); // TODO: correct?
}

result_t Kernel::ConnectToNamedPort(const std::string_view name,
Expand Down
9 changes: 8 additions & 1 deletion src/core/horizon/services/account/internal/user.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ namespace hydra::horizon::services::account::internal {

constexpr uuid_t INVALID_USER_ID = 0x0;

inline u64 GetTimestamp() {
return static_cast<u64>(
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count());
}

class User {
friend class UserManager;

Expand Down Expand Up @@ -66,7 +73,7 @@ class User {
std::string avatar_path;

// Helpers
void NotifyEdit() { base.last_edit_timestamp = get_absolute_time(); }
void NotifyEdit() { base.last_edit_timestamp = GetTimestamp(); }

public:
CONST_REF_GETTER(base, GetBase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ void UserManager::Serialize(uuid_t user_id) {
}
ofs.close();

user_pair.second = get_absolute_time();
user_pair.second = GetTimestamp();
}

void UserManager::Deserialize(uuid_t user_id) {
Expand Down
5 changes: 3 additions & 2 deletions src/core/horizon/services/nvdrv/ioctl/nvhost_ctrl_gpu.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "core/horizon/services/nvdrv/ioctl/nvhost_ctrl_gpu.hpp"

#include "core/hw/wall_clock.hpp"

namespace hydra::horizon::services::nvdrv::ioctl {

DEFINE_IOCTL_TABLE(NvHostCtrlGpu,
Expand Down Expand Up @@ -159,8 +161,7 @@ NvResult NvHostCtrlGpu::PmuGetGpuLoad(u32* out_load) {

NvResult NvHostCtrlGpu::GetGpuTime(u64* out_timestamp,
[[maybe_unused]] u64* _out_reserved) {
// TODO: is it okay to just return Mmu time?
*out_timestamp = get_absolute_time();
*out_timestamp = hw::WallClock::GetInstance().GetGpuTick();
return NvResult::Success;
}

Expand Down
5 changes: 3 additions & 2 deletions src/core/horizon/services/pcv/pcv_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "core/horizon/services/const.hpp"
#include "core/hw/tegra_x1/cpu/const.hpp"
#include "core/hw/wall_clock.hpp"

namespace hydra::horizon::services::pcv {

Expand Down Expand Up @@ -105,8 +106,8 @@ class IPcvService : public IService {

private:
// TODO: other clock rates
u32 clock_rates[(u32)ModuleId::Count] = {hw::tegra_x1::cpu::CLOCK_RATE_HZ,
0};
u32 clock_rates[(u32)ModuleId::Count] = {hw::GUEST_CNTFRQ,
hw::GPU_TICK_FREQ};

// Commands
result_t SetClockRate(ModuleId module_id, u32 rate);
Expand Down
2 changes: 0 additions & 2 deletions src/core/hw/tegra_x1/cpu/const.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ constexpr usize GUEST_PAGE_SIZE = 0x1000;

constexpr u32 MAX_STACK_TRACE_DEPTH = 32;

constexpr u32 CLOCK_RATE_HZ = 19'200'000;

} // namespace hydra::hw::tegra_x1::cpu
5 changes: 3 additions & 2 deletions src/core/hw/tegra_x1/cpu/dynarmic/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <dynarmic/interface/exclusive_monitor.h>

#include "core/hw/tegra_x1/cpu/dynarmic/mmu.hpp"
#include "core/hw/wall_clock.hpp"
#include "dynarmic/interface/optimization_flags.h"

ENABLE_ENUM_FORMATTING(Dynarmic::A64::Exception, UnallocatedEncoding,
Expand Down Expand Up @@ -43,7 +44,7 @@ Thread::Thread(IMmu* mmu, const ThreadCallbacks& callbacks, IMemory* tls_mem,
config.tpidr_el0 = &tpidr_el0; // TODO: what is this?
config.dczid_el0 = 4;
config.ctr_el0 = 0x8444c004;
config.cntfrq_el0 = CLOCK_RATE_HZ;
config.cntfrq_el0 = GUEST_CNTFRQ;

// Unpredictable instructions
config.define_unpredictable_behaviour = true;
Expand Down Expand Up @@ -166,7 +167,7 @@ void Thread::ExceptionRaised([[maybe_unused]] u64 pc,
jit->HaltExecution();
}

u64 Thread::GetCNTPCT() { return get_absolute_time(); }
u64 Thread::GetCNTPCT() { return WallClock::GetInstance().GetCntpct(); }

void Thread::SerializeState() {
for (u32 i = 0; i < 29; i++)
Expand Down
9 changes: 4 additions & 5 deletions src/core/hw/tegra_x1/cpu/hypervisor/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "core/debugger/debugger_manager.hpp"
#include "core/hw/tegra_x1/cpu/hypervisor/cpu.hpp"
#include "core/hw/tegra_x1/cpu/hypervisor/mmu.hpp"
#include "core/hw/wall_clock.hpp"

#define CPU (*static_cast<Cpu*>(&CPU_INSTANCE))
#define MMU (*static_cast<Mmu*>(mmu))
Expand Down Expand Up @@ -336,12 +337,10 @@ void Thread::InstructionTrap(u32 esr) {
// Op0 Op2 Op1 CRn 00000 CRm
switch ((esr >> 1) & 0x1ffe0f) {
case 0b11'000'011'1110'00000'0000: // CNTFRQ_EL0
ONCE(LOG_WARN(Hypervisor, "Frequency"));
// TODO: correct?
state.r[rt] = CLOCK_RATE_HZ;
state.r[rt] = GUEST_CNTFRQ;
break;
case 0b11'001'011'1110'00000'0000: // CNTPCT_EL0
state.r[rt] = get_absolute_time(); // TODO: correct?
case 0b11'001'011'1110'00000'0000: // CNTPCT_EL0
state.r[rt] = WallClock::GetInstance().GetCntpct();
break;
default:
LOG_FATAL(Hypervisor,
Expand Down
35 changes: 35 additions & 0 deletions src/core/hw/wall_clock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "core/hw/wall_clock.hpp"

namespace hydra::hw {

namespace {

u128 GetFactor(u64 num, u64 den) {
return (static_cast<u128>(num) << 64) / den;
}

u64 MultiplyByFactor(u64 num, u128 factor) { return (num * factor) >> 64; }

} // namespace

SINGLETON_DEFINE_GET_INSTANCE(WallClock, Other)

WallClock::WallClock() {
SINGLETON_SET_INSTANCE(WallClock, Other);

const auto host_freq = GetSystemFrequency();
guest_factor = GetFactor(GUEST_CNTFRQ, host_freq);
gpu_tick_factor = GetFactor(GPU_TICK_FREQ, host_freq);
}

WallClock::~WallClock() { SINGLETON_UNSET_INSTANCE(); }

u64 WallClock::GetCntpct() const {
return MultiplyByFactor(GetSystemTick(), guest_factor);
}

u64 WallClock::GetGpuTick() const {
return MultiplyByFactor(GetSystemTick(), gpu_tick_factor);
}

} // namespace hydra::hw
23 changes: 23 additions & 0 deletions src/core/hw/wall_clock.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

namespace hydra::hw {

constexpr u32 GUEST_CNTFRQ = 19'200'000;
constexpr u64 GPU_TICK_FREQ = 614'400'000;

class WallClock {
public:
static WallClock& GetInstance();

WallClock();
~WallClock();

u64 GetCntpct() const;
u64 GetGpuTick() const;

private:
u128 guest_factor;
u128 gpu_tick_factor;
};

} // namespace hydra::hw