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),
|
fNode(node),
|
||||||
fFrameworkVersion(0),
|
fFrameworkVersion(0),
|
||||||
fMessageVersion(0),
|
fMessageVersion(0),
|
||||||
|
fHyperV(NULL),
|
||||||
|
fHyperVCookie(NULL),
|
||||||
fPacket(NULL),
|
fPacket(NULL),
|
||||||
fPacketLength(packetLength),
|
fPacketLength(packetLength),
|
||||||
fMessageType(messageType),
|
fMessageType(messageType),
|
||||||
fMessageVersions(messageVersions),
|
fMessageVersions(messageVersions),
|
||||||
fMessageVersionCount(messageVersionCount),
|
fMessageVersionCount(messageVersionCount)
|
||||||
fHyperV(NULL),
|
|
||||||
fHyperVCookie(NULL)
|
|
||||||
{
|
{
|
||||||
CALLED();
|
CALLED();
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ protected:
|
|||||||
uint32 fFrameworkVersion;
|
uint32 fFrameworkVersion;
|
||||||
uint32 fMessageVersion;
|
uint32 fMessageVersion;
|
||||||
|
|
||||||
|
hyperv_device_interface* fHyperV;
|
||||||
|
hyperv_device fHyperVCookie;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
status_t _NegotiateProtocol(hv_ic_msg_negotiate* message);
|
status_t _NegotiateProtocol(hv_ic_msg_negotiate* message);
|
||||||
static void _CallbackHandler(void* arg);
|
static void _CallbackHandler(void* arg);
|
||||||
@@ -50,9 +53,6 @@ private:
|
|||||||
uint16 fMessageType;
|
uint16 fMessageType;
|
||||||
const uint32* fMessageVersions;
|
const uint32* fMessageVersions;
|
||||||
uint32 fMessageVersionCount;
|
uint32 fMessageVersionCount;
|
||||||
|
|
||||||
hyperv_device_interface* fHyperV;
|
|
||||||
hyperv_device fHyperVCookie;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ module_dependency module_dependencies[] = {
|
|||||||
|
|
||||||
module_info* modules[] = {
|
module_info* modules[] = {
|
||||||
(module_info*)&gHyperVHeartbeatDriverModule,
|
(module_info*)&gHyperVHeartbeatDriverModule,
|
||||||
|
(module_info*)&gHyperVTimeSyncDriverModule,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
extern device_manager_info* gDeviceManager;
|
extern device_manager_info* gDeviceManager;
|
||||||
|
|
||||||
extern driver_module_info gHyperVHeartbeatDriverModule;
|
extern driver_module_info gHyperVHeartbeatDriverModule;
|
||||||
|
extern driver_module_info gHyperVTimeSyncDriverModule;
|
||||||
|
|
||||||
|
|
||||||
#endif // _HYPERV_IC_DRIVER_H_
|
#endif // _HYPERV_IC_DRIVER_H_
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ KernelAddon hyperv_ic :
|
|||||||
|
|
||||||
:
|
:
|
||||||
hyperv_ic_heartbeat.a
|
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 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