hyperv_ic: Add time sync support
Adds support for the time sync Hyper-V integration component. Change-Id: If8617feab196e59b8e1d2447ffbcb344a0e66f38 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10503 Tested-by: Commit checker robot <[email protected]> Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,5 +18,6 @@ module_dependency module_dependencies[] = {
|
||||
|
||||
module_info* modules[] = {
|
||||
(module_info*)&gHyperVHeartbeatDriverModule,
|
||||
(module_info*)&gHyperVTimeSyncDriverModule,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
extern device_manager_info* gDeviceManager;
|
||||
|
||||
extern driver_module_info gHyperVHeartbeatDriverModule;
|
||||
extern driver_module_info gHyperVTimeSyncDriverModule;
|
||||
|
||||
|
||||
#endif // _HYPERV_IC_DRIVER_H_
|
||||
|
||||
@@ -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 ;
|
||||
|
||||
@@ -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
|
||||
;
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2026 John Davis. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <real_time_clock.h>
|
||||
|
||||
#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<hv_timesync_msg_v4*>(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<hv_timesync_msg_v1*>(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);
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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<TimeSync*>(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
|
||||
};
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user