diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.cpp b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.cpp index 4ae4caf1bf..5f62806d17 100644 --- a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.cpp +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.cpp @@ -25,13 +25,13 @@ ICBase::ICBase(device_node* node, uint32 packetLength, uint16 messageType, fNode(node), fFrameworkVersion(0), fMessageVersion(0), + fHyperV(NULL), + fHyperVCookie(NULL), fPacket(NULL), fPacketLength(packetLength), fMessageType(messageType), fMessageVersions(messageVersions), - fMessageVersionCount(messageVersionCount), - fHyperV(NULL), - fHyperVCookie(NULL) + fMessageVersionCount(messageVersionCount) { CALLED(); diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.h b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.h index a3d0179f25..7521bebae7 100644 --- a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.h +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICBase.h @@ -39,6 +39,9 @@ protected: uint32 fFrameworkVersion; uint32 fMessageVersion; + hyperv_device_interface* fHyperV; + hyperv_device fHyperVCookie; + private: status_t _NegotiateProtocol(hv_ic_msg_negotiate* message); static void _CallbackHandler(void* arg); @@ -50,9 +53,6 @@ private: uint16 fMessageType; const uint32* fMessageVersions; uint32 fMessageVersionCount; - - hyperv_device_interface* fHyperV; - hyperv_device fHyperVCookie; }; diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.cpp b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.cpp index e4fd4ba0cf..2e333799dd 100644 --- a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.cpp +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.cpp @@ -18,5 +18,6 @@ module_dependency module_dependencies[] = { module_info* modules[] = { (module_info*)&gHyperVHeartbeatDriverModule, + (module_info*)&gHyperVTimeSyncDriverModule, NULL }; diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.h b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.h index d9c657a591..941d930e33 100644 --- a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.h +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/ICDriver.h @@ -13,6 +13,7 @@ extern device_manager_info* gDeviceManager; extern driver_module_info gHyperVHeartbeatDriverModule; +extern driver_module_info gHyperVTimeSyncDriverModule; #endif // _HYPERV_IC_DRIVER_H_ diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/Jamfile b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/Jamfile index 9f8df40845..97ae82d9d2 100644 --- a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/Jamfile +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/Jamfile @@ -9,6 +9,8 @@ KernelAddon hyperv_ic : : hyperv_ic_heartbeat.a + hyperv_ic_timesync.a ; SubInclude HAIKU_TOP src add-ons kernel drivers hyperv hyperv_ic heartbeat ; +SubInclude HAIKU_TOP src add-ons kernel drivers hyperv hyperv_ic timesync ; diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/Jamfile b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/Jamfile new file mode 100644 index 0000000000..c3e1711928 --- /dev/null +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/Jamfile @@ -0,0 +1,11 @@ +SubDir HAIKU_TOP src add-ons kernel drivers hyperv hyperv_ic timesync ; + +UsePrivateKernelHeaders ; +UsePrivateHeaders drivers hyperv ; + +SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ; + +KernelStaticLibrary hyperv_ic_timesync : + TimeSync.cpp + TimeSyncModule.cpp + ; diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSync.cpp b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSync.cpp new file mode 100644 index 0000000000..2752d73cf4 --- /dev/null +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSync.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2026 John Davis. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include "TimeSync.h" + + +TimeSync::TimeSync(device_node* node) + : ICBase(node, HV_TIMESYNC_PKT_BUFFER_SIZE, HV_IC_MSGTYPE_TIMESYNC, hv_timesync_versions, + hv_timesync_version_count) +{ + CALLED(); + fStatus = Connect(HV_TIMESYNC_RING_SIZE, HV_TIMESYNC_RING_SIZE); +} + + +void +TimeSync::OnProtocolNegotiated() +{ + TRACE("Time sync protocol version %u.%u\n", GET_IC_VERSION_MAJOR(fMessageVersion), + GET_IC_VERSION_MINOR(fMessageVersion)); +} + + +void +TimeSync::OnMessageReceived(hv_ic_msg* icMessage) +{ + if (fMessageVersion >= HV_TIMESYNC_VERSION_V4_0) { + hv_timesync_msg_v4* message = reinterpret_cast(icMessage); + if (message->header.data_length < offsetof(hv_timesync_msg_v4, stratum) + - sizeof(message->header)) { + ERROR("Time sync msg invalid length 0x%X\n", message->header.data_length); + message->header.status = HV_IC_STATUS_FAILED; + return; + } + _SyncTime(message->parent_time, message->reference_time, message->flags); + } else { + hv_timesync_msg_v1* message = reinterpret_cast(icMessage); + if (message->header.data_length < offsetof(hv_timesync_msg_v1, flags) + - sizeof(message->header)) { + ERROR("Time sync msg invalid length 0x%X\n", message->header.data_length); + message->header.status = HV_IC_STATUS_FAILED; + return; + } + _SyncTime(message->parent_time, 0, message->flags); + } +} + + +void +TimeSync::_SyncTime(uint64 hostTime, uint64 referenceTime, uint8 flags) +{ + TRACE("New host time %" B_PRIu64 " ref time %" B_PRIu64 " flags 0x%X\n", hostTime, + referenceTime, flags); + if ((flags & (HV_TIMESYNC_MSGFLAGS_SYNC | HV_TIMESYNC_MSGFLAGS_SAMPLE)) == 0) + return; + + // Calculate adjustment based on reference counter if supported + uint64 timeAdjust = 0; + if (referenceTime != 0) { + uint64 refCount; + if (fHyperV->get_reference_counter(fHyperVCookie, &refCount) == B_OK) { + timeAdjust = refCount - referenceTime; + TRACE("Reference counter adjustment by %" B_PRIu64 "\n", timeAdjust); + } + } + + // Time provided by Hyper-V is always UTC + bigtime_t newTime = (hostTime - HV_TIMESYNC_TIME_BASE + timeAdjust) / 10LL; + TRACE("New time %" B_PRIdBIGTIME "\n", newTime); + + if ((flags & HV_TIMESYNC_MSGFLAGS_SAMPLE) != 0) { + bigtime_t currentTime = real_time_clock_usecs(); + TRACE("Current time %" B_PRIdBIGTIME "\n", currentTime); + + bigtime_t diffTime = (newTime > currentTime) ? (newTime - currentTime) + : (currentTime - newTime); + TRACE("Time diff %" B_PRIdBIGTIME "\n", diffTime); + if (diffTime <= HV_MS_TO_US(500)) + return; + } + + // System time will be changed if explicitly requested, or the difference is too great + TRACE("Set system time to %" B_PRIdBIGTIME "\n", newTime); + set_real_time_clock_usecs(newTime); +} diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSync.h b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSync.h new file mode 100644 index 0000000000..e9c3d6c81c --- /dev/null +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSync.h @@ -0,0 +1,40 @@ +/* + * Copyright 2026 John Davis. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _HYPERV_TIMESYNC_H_ +#define _HYPERV_TIMESYNC_H_ + + +#include "ICBase.h" + +#include "TimeSyncProtocol.h" + + +//#define TRACE_HYPERV_TIMESYNC +#ifdef TRACE_HYPERV_TIMESYNC +# define TRACE(x...) dprintf("\33[94mhyperv_timesync:\33[0m " x) +#else +# define TRACE(x...) ; +#endif +#define TRACE_ALWAYS(x...) dprintf("\33[94mhyperv_timesync:\33[0m " x) +#define ERROR(x...) dprintf("\33[94mhyperv_timesync:\33[0m " x) +#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + +#define HYPERV_TIMESYNC_DRIVER_MODULE_NAME "drivers/hyperv/hyperv_ic/timesync/driver_v1" + + +class TimeSync : public ICBase { +public: + TimeSync(device_node* node); + +protected: + void OnProtocolNegotiated(); + void OnMessageReceived(hv_ic_msg* icMessage); + +private: + void _SyncTime(uint64 hostTime, uint64 referenceTime, uint8 flags); +}; + + +#endif // _HYPERV_TIMESYNC_H_ diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSyncModule.cpp b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSyncModule.cpp new file mode 100644 index 0000000000..594dabde8b --- /dev/null +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSyncModule.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2026 John Davis. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "TimeSync.h" + + +static float +hyperv_timesync_supports_device(device_node* parent) +{ + CALLED(); + + // Check if parent is the Hyper-V bus manager + const char* bus; + if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) != B_OK) + return -1; + + if (strcmp(bus, HYPERV_BUS_NAME) != 0) + return 0.0f; + + // Check if parent is a Hyper-V time sync device + const char* type; + if (gDeviceManager->get_attr_string(parent, HYPERV_DEVICE_TYPE_STRING_ITEM, &type, false) + != B_OK) + return 0.0f; + + if (strcmp(type, VMBUS_TYPE_TIMESYNC) != 0) + return 0.0f; + + TRACE("Hyper-V Time Sync device found!\n"); + return 0.8f; +} + + +static status_t +hyperv_timesync_register_device(device_node* parent) +{ + CALLED(); + + device_attr attributes[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, + { .string = HYPERV_PRETTYNAME_TIMESYNC }}, + { NULL } + }; + + return gDeviceManager->register_node(parent, HYPERV_TIMESYNC_DRIVER_MODULE_NAME, attributes, + NULL, NULL); +} + + +static status_t +hyperv_timesync_init_driver(device_node* node, void** _driverCookie) +{ + CALLED(); + + TimeSync* timeSync = new(std::nothrow) TimeSync(node); + if (timeSync == NULL) { + ERROR("Unable to allocate Hyper-V Time Sync object\n"); + return B_NO_MEMORY; + } + + status_t status = timeSync->InitCheck(); + if (status != B_OK) { + ERROR("Failed to set up Hyper-V Time Sync object\n"); + delete timeSync; + return status; + } + TRACE("Hyper-V Time Sync object created\n"); + + *_driverCookie = timeSync; + return B_OK; +} + + +static void +hyperv_timesync_uninit_driver(void* driverCookie) +{ + CALLED(); + TimeSync* timeSync = reinterpret_cast(driverCookie); + delete timeSync; +} + + +driver_module_info gHyperVTimeSyncDriverModule = { + { + HYPERV_TIMESYNC_DRIVER_MODULE_NAME, + 0, + NULL + }, + + hyperv_timesync_supports_device, + hyperv_timesync_register_device, + hyperv_timesync_init_driver, + hyperv_timesync_uninit_driver, + NULL, // register child devices + NULL, // rescan + NULL // removed +}; diff --git a/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSyncProtocol.h b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSyncProtocol.h new file mode 100644 index 0000000000..74651b4644 --- /dev/null +++ b/src/add-ons/kernel/drivers/hyperv/hyperv_ic/timesync/TimeSyncProtocol.h @@ -0,0 +1,62 @@ +/* + * Copyright 2026 John Davis. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _HYPERV_TIMESYNC_PROTOCOL_H_ +#define _HYPERV_TIMESYNC_PROTOCOL_H_ + + +#include "ICProtocol.h" + + +#define HV_TIMESYNC_RING_SIZE 0x1000 +#define HV_TIMESYNC_PKT_BUFFER_SIZE 128 + + +// Time sync versions +#define HV_TIMESYNC_VERSION_V1_0 MAKE_IC_VERSION(1, 0) +#define HV_TIMESYNC_VERSION_V3_0 MAKE_IC_VERSION(3, 0) +#define HV_TIMESYNC_VERSION_V4_0 MAKE_IC_VERSION(4, 0) + + +static const uint32 hv_timesync_versions[] = { + HV_TIMESYNC_VERSION_V4_0, + HV_TIMESYNC_VERSION_V3_0, + HV_TIMESYNC_VERSION_V1_0 +}; +static const uint32 hv_timesync_version_count = sizeof(hv_timesync_versions) + / sizeof(hv_timesync_versions[0]); + + +#define HV_TIMESYNC_TIME_BASE 116444736000000000ULL + +// Time sync message flags +#define HV_TIMESYNC_MSGFLAGS_SYNC (1 << 0) +#define HV_TIMESYNC_MSGFLAGS_SAMPLE (1 << 1) + + +// Time sync message (versions 1 and 3) +typedef struct { + hv_ic_msg_header header; + + uint64 parent_time; + uint64 child_time; + uint64 round_trip_time; + uint8 flags; +} _PACKED hv_timesync_msg_v1; + + +// Time sync message (version 4) +typedef struct { + hv_ic_msg_header header; + + uint64 parent_time; + uint64 reference_time; + uint8 flags; + uint8 leap_flags; + uint8 stratum; + uint8 reserved[3]; +} _PACKED hv_timesync_msg_v4; + + +#endif \ No newline at end of file