hyperv: Implement heartbeat support
Adds support for the Hyper-V heartbeat integration component as part of a new hyperv_ic driver. Change-Id: I130ccc946d1f3ea30d87abf403d23c2cc34a1c57 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10427 Tested-by: Commit checker robot <[email protected]> Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
@@ -665,6 +665,7 @@ rule ArchitectureSetupWarnings architecture
|
|||||||
EnableWerror src add-ons kernel drivers display ;
|
EnableWerror src add-ons kernel drivers display ;
|
||||||
EnableWerror src add-ons kernel drivers dvb ;
|
EnableWerror src add-ons kernel drivers dvb ;
|
||||||
EnableWerror src add-ons kernel drivers graphics ;
|
EnableWerror src add-ons kernel drivers graphics ;
|
||||||
|
EnableWerror src add-ons kernel drivers hyperv ;
|
||||||
EnableWerror src add-ons kernel drivers input ;
|
EnableWerror src add-ons kernel drivers input ;
|
||||||
EnableWerror src add-ons kernel drivers joystick ;
|
EnableWerror src add-ons kernel drivers joystick ;
|
||||||
EnableWerror src add-ons kernel drivers midi ;
|
EnableWerror src add-ons kernel drivers midi ;
|
||||||
|
|||||||
@@ -154,6 +154,9 @@ AddNewDriversToPackage display :
|
|||||||
AddNewDriversToPackage audio hmulti :
|
AddNewDriversToPackage audio hmulti :
|
||||||
# virtio_sound
|
# virtio_sound
|
||||||
;
|
;
|
||||||
|
AddNewDriversToPackage hyperv :
|
||||||
|
hyperv_ic@x86,x86_64
|
||||||
|
;
|
||||||
|
|
||||||
# legacy drivers
|
# legacy drivers
|
||||||
AddDriversToPackage :
|
AddDriversToPackage :
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ AddNewDriversToPackage network :
|
|||||||
AddNewDriversToPackage display :
|
AddNewDriversToPackage display :
|
||||||
# display_adapter@x86
|
# display_adapter@x86
|
||||||
;
|
;
|
||||||
|
AddNewDriversToPackage hyperv :
|
||||||
|
hyperv_ic@x86,x86_64
|
||||||
|
;
|
||||||
|
|
||||||
# legacy drivers
|
# legacy drivers
|
||||||
AddDriversToPackage :
|
AddDriversToPackage :
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers common ;
|
|||||||
SubInclude HAIKU_TOP src add-ons kernel drivers disk ;
|
SubInclude HAIKU_TOP src add-ons kernel drivers disk ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel drivers display ;
|
SubInclude HAIKU_TOP src add-ons kernel drivers display ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel drivers dvb ;
|
SubInclude HAIKU_TOP src add-ons kernel drivers dvb ;
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel drivers hyperv ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel drivers input ;
|
SubInclude HAIKU_TOP src add-ons kernel drivers input ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel drivers joystick ;
|
SubInclude HAIKU_TOP src add-ons kernel drivers joystick ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel drivers graphics ;
|
SubInclude HAIKU_TOP src add-ons kernel drivers graphics ;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel drivers hyperv ;
|
||||||
|
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel drivers hyperv hyperv_ic ;
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ICBase.h"
|
||||||
|
|
||||||
|
|
||||||
|
//#define TRACE_HYPERV_IC
|
||||||
|
#ifdef TRACE_HYPERV_IC
|
||||||
|
# define TRACE(x...) dprintf("\33[94mhyperv_ic:\33[0m " x)
|
||||||
|
#else
|
||||||
|
# define TRACE(x...) ;
|
||||||
|
#endif
|
||||||
|
#define TRACE_ALWAYS(x...) dprintf("\33[94mhyperv_ic:\33[0m " x)
|
||||||
|
#define ERROR(x...) dprintf("\33[94mhyperv_ic:\33[0m " x)
|
||||||
|
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
||||||
|
|
||||||
|
|
||||||
|
ICBase::ICBase(device_node* node, uint32 packetLength, uint16 messageType,
|
||||||
|
const uint32* messageVersions, uint32 messageVersionCount)
|
||||||
|
:
|
||||||
|
fStatus(B_NO_INIT),
|
||||||
|
fNode(node),
|
||||||
|
fFrameworkVersion(0),
|
||||||
|
fMessageVersion(0),
|
||||||
|
fPacket(NULL),
|
||||||
|
fPacketLength(packetLength),
|
||||||
|
fMessageType(messageType),
|
||||||
|
fMessageVersions(messageVersions),
|
||||||
|
fMessageVersionCount(messageVersionCount),
|
||||||
|
fHyperV(NULL),
|
||||||
|
fHyperVCookie(NULL)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
|
device_node* parent = gDeviceManager->get_parent_node(node);
|
||||||
|
gDeviceManager->get_driver(parent, (driver_module_info**)&fHyperV, (void**)&fHyperVCookie);
|
||||||
|
gDeviceManager->put_node(parent);
|
||||||
|
|
||||||
|
fPacket = static_cast<uint8*>(malloc(fPacketLength));
|
||||||
|
if (fPacket == NULL) {
|
||||||
|
fStatus = B_NO_MEMORY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fStatus = B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ICBase::~ICBase()
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
|
free(fPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ICBase::Connect(uint32 txLength, uint32 rxLength)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
|
status_t status = fHyperV->open(fHyperVCookie, txLength, rxLength, _CallbackHandler, this);
|
||||||
|
if (status != B_OK) {
|
||||||
|
ERROR("Failed to open channel");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ICBase::Disconnect()
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
fHyperV->close(fHyperVCookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*virtual*/ void
|
||||||
|
ICBase::OnProtocolNegotiated()
|
||||||
|
{
|
||||||
|
// Default implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*virtual*/ void
|
||||||
|
ICBase::OnMessageSent(hv_ic_msg* icMessage)
|
||||||
|
{
|
||||||
|
// Default implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ICBase::_NegotiateProtocol(hv_ic_msg_negotiate* message)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
|
if (message->header.data_length < offsetof(hv_ic_msg_negotiate, versions[2])
|
||||||
|
- sizeof(message->header)) {
|
||||||
|
ERROR("IC[%u] invalid negotiate msg length 0x%X\n", fMessageType,
|
||||||
|
message->header.data_length);
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message->framework_version_count == 0 || message->message_version_count == 0) {
|
||||||
|
ERROR("IC[%u] invalid negotiate msg version count\n", fMessageType);
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 versionCount = message->framework_version_count + message->message_version_count;
|
||||||
|
if (versionCount < 2) {
|
||||||
|
ERROR("IC[%u] invalid negotiate msg version count\n", fMessageType);
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match the highest supported framework version
|
||||||
|
bool foundFrameworkVersion = false;
|
||||||
|
for (uint32 i = 0; i < hv_framework_version_count; i++) {
|
||||||
|
for (uint32 j = 0; j < message->framework_version_count; j++) {
|
||||||
|
TRACE("IC[%u] checking fw version %u.%u against %u.%u\n", fMessageType,
|
||||||
|
GET_IC_VERSION_MAJOR(hv_framework_versions[i]),
|
||||||
|
GET_IC_VERSION_MINOR(hv_framework_versions[i]),
|
||||||
|
GET_IC_VERSION_MAJOR(message->versions[j]),
|
||||||
|
GET_IC_VERSION_MINOR(message->versions[j]));
|
||||||
|
|
||||||
|
if (hv_framework_versions[i] == message->versions[j]) {
|
||||||
|
fFrameworkVersion = hv_framework_versions[i];
|
||||||
|
foundFrameworkVersion = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundFrameworkVersion)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match the highest supported message version
|
||||||
|
bool foundMessageVersion = false;
|
||||||
|
for (uint32 i = 0; i < fMessageVersionCount; i++) {
|
||||||
|
for (uint32 j = message->message_version_count; j < versionCount; j++) {
|
||||||
|
TRACE("IC[%u] checking msg version %u.%u against %u.%u\n", fMessageType,
|
||||||
|
GET_IC_VERSION_MAJOR(fMessageVersions[i]),
|
||||||
|
GET_IC_VERSION_MINOR(fMessageVersions[i]),
|
||||||
|
GET_IC_VERSION_MAJOR(message->versions[j]),
|
||||||
|
GET_IC_VERSION_MINOR(message->versions[j]));
|
||||||
|
|
||||||
|
if (fMessageVersions[i] == message->versions[j]) {
|
||||||
|
fMessageVersion = fMessageVersions[i];
|
||||||
|
foundMessageVersion = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundMessageVersion)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundFrameworkVersion || !foundMessageVersion) {
|
||||||
|
ERROR("IC%u unsupported versions\n", fMessageType);
|
||||||
|
message->framework_version_count = 0;
|
||||||
|
message->message_version_count = 0;
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("IC[%u] found supported fw version %u.%u msg version %u.%u\n", fMessageType,
|
||||||
|
GET_IC_VERSION_MAJOR(fFrameworkVersion), GET_IC_VERSION_MINOR(fFrameworkVersion),
|
||||||
|
GET_IC_VERSION_MAJOR(fMessageVersion), GET_IC_VERSION_MINOR(fMessageVersion));
|
||||||
|
|
||||||
|
message->framework_version_count = 1;
|
||||||
|
message->message_version_count = 1;
|
||||||
|
message->versions[0] = fFrameworkVersion;
|
||||||
|
message->versions[1] = fMessageVersion;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*static*/ void
|
||||||
|
ICBase::_CallbackHandler(void* arg)
|
||||||
|
{
|
||||||
|
ICBase* icBaseDevice = reinterpret_cast<ICBase*>(arg);
|
||||||
|
icBaseDevice->_Callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ICBase::_Callback()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
uint32 length = fPacketLength;
|
||||||
|
uint32 headerLength;
|
||||||
|
uint32 dataLength;
|
||||||
|
|
||||||
|
status_t status = fHyperV->read_packet(fHyperVCookie, fPacket, &length,
|
||||||
|
&headerLength, &dataLength);
|
||||||
|
if (status == B_DEV_NOT_READY) {
|
||||||
|
break;
|
||||||
|
} else if (status != B_OK) {
|
||||||
|
ERROR("IC[%u] failed to read packet (%s)\n", fMessageType, strerror(status));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataLength < sizeof(hv_ic_msg_header)) {
|
||||||
|
ERROR("IC[%u] invalid packet\n", fMessageType);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vmbus_pkt_header* header = reinterpret_cast<vmbus_pkt_header*>(fPacket);
|
||||||
|
hv_ic_msg* message = reinterpret_cast<hv_ic_msg*>(fPacket + headerLength);
|
||||||
|
if (message->header.data_length <= dataLength - sizeof(message->header)) {
|
||||||
|
if (message->header.type == HV_IC_MSGTYPE_NEGOTIATE) {
|
||||||
|
// IC protocol negotiation
|
||||||
|
status = _NegotiateProtocol(&message->negotiate);
|
||||||
|
if (status == B_OK) {
|
||||||
|
OnProtocolNegotiated();
|
||||||
|
} else {
|
||||||
|
ERROR("IC[%u] protocol negotiation failed (%s)\n", fMessageType,
|
||||||
|
strerror(status));
|
||||||
|
message->header.status = HV_IC_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
} else if (message->header.type == fMessageType) {
|
||||||
|
// IC device-specific message
|
||||||
|
OnMessageReceived(message);
|
||||||
|
} else {
|
||||||
|
ERROR("IC[%u] unknown message type %u\n", fMessageType, message->header.type);
|
||||||
|
message->header.status = HV_IC_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ERROR("IC[%u] invalid msg data length 0x%X pkt length 0x%X\n", fMessageType,
|
||||||
|
message->header.data_length, dataLength);
|
||||||
|
message->header.status = HV_IC_STATUS_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always respond to Hyper-V with the same packet that was originally received
|
||||||
|
message->header.flags = HV_IC_FLAG_TRANSACTION | HV_IC_FLAG_RESPONSE;
|
||||||
|
status = fHyperV->write_packet(fHyperVCookie, VMBUS_PKTTYPE_DATA_INBAND, message,
|
||||||
|
sizeof(hv_ic_msg_header) + message->header.data_length, false, header->transaction_id);
|
||||||
|
|
||||||
|
if (status == B_OK)
|
||||||
|
OnMessageSent(message);
|
||||||
|
else
|
||||||
|
ERROR("IC[%u] failed to send message (%s)\n", fMessageType, strerror(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _HYPERV_IC_BASE_H_
|
||||||
|
#define _HYPERV_IC_BASE_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <hyperv.h>
|
||||||
|
#include <hyperv_spec.h>
|
||||||
|
|
||||||
|
#include "ICDriver.h"
|
||||||
|
#include "ICProtocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ICBase {
|
||||||
|
public:
|
||||||
|
ICBase(device_node* node, uint32 packetLength, uint16 messageType,
|
||||||
|
const uint32* messageVersions, uint32 messageVersionCount);
|
||||||
|
virtual ~ICBase();
|
||||||
|
status_t InitCheck() const { return fStatus; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
status_t Connect(uint32 txLength, uint32 rxLength);
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
virtual void OnProtocolNegotiated();
|
||||||
|
virtual void OnMessageReceived(hv_ic_msg* icMessage) = 0;
|
||||||
|
virtual void OnMessageSent(hv_ic_msg* icMessage);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
status_t fStatus;
|
||||||
|
device_node* fNode;
|
||||||
|
uint32 fFrameworkVersion;
|
||||||
|
uint32 fMessageVersion;
|
||||||
|
|
||||||
|
private:
|
||||||
|
status_t _NegotiateProtocol(hv_ic_msg_negotiate* message);
|
||||||
|
static void _CallbackHandler(void* arg);
|
||||||
|
void _Callback();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8* fPacket;
|
||||||
|
uint32 fPacketLength;
|
||||||
|
uint16 fMessageType;
|
||||||
|
const uint32* fMessageVersions;
|
||||||
|
uint32 fMessageVersionCount;
|
||||||
|
|
||||||
|
hyperv_device_interface* fHyperV;
|
||||||
|
hyperv_device fHyperVCookie;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _HYPERV_IC_BASE_H_
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ICDriver.h"
|
||||||
|
|
||||||
|
|
||||||
|
device_manager_info* gDeviceManager;
|
||||||
|
|
||||||
|
|
||||||
|
module_dependency module_dependencies[] = {
|
||||||
|
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module_info* modules[] = {
|
||||||
|
(module_info*)&gHyperVHeartbeatDriverModule,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _HYPERV_IC_DRIVER_H_
|
||||||
|
#define _HYPERV_IC_DRIVER_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <device_manager.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern device_manager_info* gDeviceManager;
|
||||||
|
|
||||||
|
extern driver_module_info gHyperVHeartbeatDriverModule;
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _HYPERV_IC_DRIVER_H_
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _HYPERV_IC_PROTOCOL_H_
|
||||||
|
#define _HYPERV_IC_PROTOCOL_H_
|
||||||
|
|
||||||
|
|
||||||
|
#define MAKE_IC_VERSION(major, minor) ((((minor) << 16) & 0xFFFF0000) | ((major) & 0x0000FFFF))
|
||||||
|
#define GET_IC_VERSION_MAJOR(version) ((version) & 0xFFFF)
|
||||||
|
#define GET_IC_VERSION_MINOR(version) (((version) >> 16) & 0xFFFF)
|
||||||
|
|
||||||
|
// IC framework versions
|
||||||
|
#define HV_IC_VERSION_2008 MAKE_IC_VERSION(1, 0)
|
||||||
|
#define HV_IC_VERSION_V3 MAKE_IC_VERSION(3, 0)
|
||||||
|
|
||||||
|
|
||||||
|
static const uint32 hv_framework_versions[] = {
|
||||||
|
HV_IC_VERSION_V3,
|
||||||
|
HV_IC_VERSION_2008
|
||||||
|
};
|
||||||
|
static const uint32 hv_framework_version_count = sizeof(hv_framework_versions)
|
||||||
|
/ sizeof(hv_framework_versions[0]);
|
||||||
|
|
||||||
|
|
||||||
|
// IC message types
|
||||||
|
enum {
|
||||||
|
HV_IC_MSGTYPE_NEGOTIATE = 0,
|
||||||
|
HV_IC_MSGTYPE_HEARTBEAT = 1,
|
||||||
|
HV_IC_MSGTYPE_KVP = 2,
|
||||||
|
HV_IC_MSGTYPE_SHUTDOWN = 3,
|
||||||
|
HV_IC_MSGTYPE_TIMESYNC = 4,
|
||||||
|
HV_IC_MSGTYPE_VSS = 5,
|
||||||
|
HV_IC_MSGTYPE_FILECOPY = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// IC message flags
|
||||||
|
#define HV_IC_FLAG_TRANSACTION (1 << 0)
|
||||||
|
#define HV_IC_FLAG_REQUEST (1 << 1)
|
||||||
|
#define HV_IC_FLAG_RESPONSE (1 << 2)
|
||||||
|
|
||||||
|
|
||||||
|
// IC status
|
||||||
|
enum {
|
||||||
|
HV_IC_STATUS_OK = 0x0,
|
||||||
|
HV_IC_STATUS_FAILED = 0x80004005,
|
||||||
|
HV_IC_STATUS_TIMEOUT = 0x800705B4,
|
||||||
|
HV_IC_STATUS_INVALID_ARG = 0x80070057,
|
||||||
|
HV_IC_STATUS_ALREADY_EXISTS = 0x80070050,
|
||||||
|
HV_IC_STATUS_DISK_FULL = 0x80070070
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// IC message header
|
||||||
|
typedef struct {
|
||||||
|
uint32 pipe_flags;
|
||||||
|
uint32 pipe_messages;
|
||||||
|
uint32 framework_version;
|
||||||
|
uint16 type;
|
||||||
|
uint32 message_version;
|
||||||
|
uint16 data_length;
|
||||||
|
uint32 status;
|
||||||
|
uint8 transaction_id;
|
||||||
|
uint8 flags;
|
||||||
|
uint16 reserved;
|
||||||
|
} _PACKED hv_ic_msg_header;
|
||||||
|
|
||||||
|
|
||||||
|
// IC negotiation message
|
||||||
|
typedef struct {
|
||||||
|
hv_ic_msg_header header;
|
||||||
|
|
||||||
|
uint16 framework_version_count;
|
||||||
|
uint16 message_version_count;
|
||||||
|
uint32 reserved;
|
||||||
|
uint32 versions[];
|
||||||
|
} _PACKED hv_ic_msg_negotiate;
|
||||||
|
|
||||||
|
|
||||||
|
// IC combined message
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
hv_ic_msg_header header;
|
||||||
|
hv_ic_msg_negotiate negotiate;
|
||||||
|
};
|
||||||
|
} _PACKED hv_ic_msg;
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _HYPERV_IC_PROTOCOL_H_
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel drivers hyperv hyperv_ic ;
|
||||||
|
|
||||||
|
UsePrivateKernelHeaders ;
|
||||||
|
UsePrivateHeaders drivers hyperv ;
|
||||||
|
|
||||||
|
KernelAddon hyperv_ic :
|
||||||
|
ICBase.cpp
|
||||||
|
ICDriver.cpp
|
||||||
|
|
||||||
|
:
|
||||||
|
hyperv_ic_heartbeat.a
|
||||||
|
;
|
||||||
|
|
||||||
|
SubInclude HAIKU_TOP src add-ons kernel drivers hyperv hyperv_ic heartbeat ;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Heartbeat.h"
|
||||||
|
|
||||||
|
|
||||||
|
Heartbeat::Heartbeat(device_node* node)
|
||||||
|
: ICBase(node, HV_HEARTBEAT_PKT_BUFFER_SIZE, HV_IC_MSGTYPE_HEARTBEAT, hv_heartbeat_versions,
|
||||||
|
hv_heartbeat_version_count)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
fStatus = Connect(HV_HEARTBEAT_RING_SIZE, HV_HEARTBEAT_RING_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Heartbeat::OnProtocolNegotiated()
|
||||||
|
{
|
||||||
|
TRACE("Heartbeat protocol version %u.%u\n", GET_IC_VERSION_MAJOR(fMessageVersion),
|
||||||
|
GET_IC_VERSION_MINOR(fMessageVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Heartbeat::OnMessageReceived(hv_ic_msg* icMessage)
|
||||||
|
{
|
||||||
|
hv_heartbeat_msg* message = reinterpret_cast<hv_heartbeat_msg*>(icMessage);
|
||||||
|
if (message->header.data_length < offsetof(hv_heartbeat_msg, sequence)
|
||||||
|
- sizeof(message->header)) {
|
||||||
|
ERROR("Heartbeat msg invalid length 0x%X\n", message->header.data_length);
|
||||||
|
message->header.status = HV_IC_STATUS_FAILED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guest and host alternate incrementing the sequence number, typically every two seconds
|
||||||
|
// Hyper-V will report the guest as healthy so long as this continues
|
||||||
|
TRACE("Heartbeat sequence %" B_PRIu64 "\n", message->sequence);
|
||||||
|
message->sequence++;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _HYPERV_HEARTBEAT_H_
|
||||||
|
#define _HYPERV_HEARTBEAT_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "ICBase.h"
|
||||||
|
|
||||||
|
#include "HeartbeatProtocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
//#define TRACE_HYPERV_HEARTBEAT
|
||||||
|
#ifdef TRACE_HYPERV_HEARTBEAT
|
||||||
|
# define TRACE(x...) dprintf("\33[94mhyperv_heartbeat:\33[0m " x)
|
||||||
|
#else
|
||||||
|
# define TRACE(x...) ;
|
||||||
|
#endif
|
||||||
|
#define TRACE_ALWAYS(x...) dprintf("\33[94mhyperv_heartbeat:\33[0m " x)
|
||||||
|
#define ERROR(x...) dprintf("\33[94mhyperv_heartbeat:\33[0m " x)
|
||||||
|
#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
|
||||||
|
|
||||||
|
#define HYPERV_HEARTBEAT_DRIVER_MODULE_NAME "drivers/hyperv/hyperv_ic/heartbeat/driver_v1"
|
||||||
|
|
||||||
|
|
||||||
|
class Heartbeat : public ICBase {
|
||||||
|
public:
|
||||||
|
Heartbeat(device_node* node);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void OnProtocolNegotiated();
|
||||||
|
void OnMessageReceived(hv_ic_msg* icMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _HYPERV_HEARTBEAT_H_
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "Heartbeat.h"
|
||||||
|
|
||||||
|
|
||||||
|
static float
|
||||||
|
hyperv_heartbeat_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 heartbeat 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_HEARTBEAT) != 0)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
TRACE("Hyper-V Heartbeat device found!\n");
|
||||||
|
return 0.8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
hyperv_heartbeat_register_device(device_node* parent)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
|
device_attr attributes[] = {
|
||||||
|
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
||||||
|
{ .string = HYPERV_PRETTYNAME_HEARTBEAT }},
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
return gDeviceManager->register_node(parent, HYPERV_HEARTBEAT_DRIVER_MODULE_NAME, attributes,
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
hyperv_heartbeat_init_driver(device_node* node, void** _driverCookie)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
|
||||||
|
Heartbeat* heartbeat = new(std::nothrow) Heartbeat(node);
|
||||||
|
if (heartbeat == NULL) {
|
||||||
|
ERROR("Unable to allocate Hyper-V Heartbeat object\n");
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t status = heartbeat->InitCheck();
|
||||||
|
if (status != B_OK) {
|
||||||
|
ERROR("Failed to set up Hyper-V Heartbeat object\n");
|
||||||
|
delete heartbeat;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
TRACE("Hyper-V Heartbeat object created\n");
|
||||||
|
|
||||||
|
*_driverCookie = heartbeat;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hyperv_heartbeat_uninit_driver(void* driverCookie)
|
||||||
|
{
|
||||||
|
CALLED();
|
||||||
|
Heartbeat* heartbeat = reinterpret_cast<Heartbeat*>(driverCookie);
|
||||||
|
delete heartbeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
driver_module_info gHyperVHeartbeatDriverModule = {
|
||||||
|
{
|
||||||
|
HYPERV_HEARTBEAT_DRIVER_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
|
||||||
|
hyperv_heartbeat_supports_device,
|
||||||
|
hyperv_heartbeat_register_device,
|
||||||
|
hyperv_heartbeat_init_driver,
|
||||||
|
hyperv_heartbeat_uninit_driver,
|
||||||
|
NULL, // register child devices
|
||||||
|
NULL, // rescan
|
||||||
|
NULL // removed
|
||||||
|
};
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 John Davis. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _HYPERV_HEARTBEAT_PROTOCOL_H_
|
||||||
|
#define _HYPERV_HEARTBEAT_PROTOCOL_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include "ICProtocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define HV_HEARTBEAT_RING_SIZE 0x1000
|
||||||
|
#define HV_HEARTBEAT_PKT_BUFFER_SIZE 128
|
||||||
|
|
||||||
|
|
||||||
|
// Heartbeat versions
|
||||||
|
#define HV_HEARTBEAT_VERSION_V1 MAKE_IC_VERSION(1, 0)
|
||||||
|
#define HV_HEARTBEAT_VERSION_V3 MAKE_IC_VERSION(3, 0)
|
||||||
|
|
||||||
|
|
||||||
|
static const uint32 hv_heartbeat_versions[] = {
|
||||||
|
HV_HEARTBEAT_VERSION_V3,
|
||||||
|
HV_HEARTBEAT_VERSION_V1
|
||||||
|
};
|
||||||
|
static const uint32 hv_heartbeat_version_count = sizeof(hv_heartbeat_versions)
|
||||||
|
/ sizeof(hv_heartbeat_versions[0]);
|
||||||
|
|
||||||
|
|
||||||
|
// Heartbeat application states
|
||||||
|
enum {
|
||||||
|
HV_HEARTBEAT_APPSTATE_NONE = 0,
|
||||||
|
HV_HEARTBEAT_APPSTATE_OK = 1,
|
||||||
|
HV_HEARTBEAT_APPSTATE_CRITICAL = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Heartbeat message
|
||||||
|
typedef struct {
|
||||||
|
hv_ic_msg_header header;
|
||||||
|
|
||||||
|
uint64 sequence;
|
||||||
|
uint64 app_state;
|
||||||
|
} _PACKED hv_heartbeat_msg;
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _HYPERV_HEARTBEAT_PROTOCOL_H_
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel drivers hyperv hyperv_ic heartbeat ;
|
||||||
|
|
||||||
|
UsePrivateKernelHeaders ;
|
||||||
|
UsePrivateHeaders drivers hyperv ;
|
||||||
|
|
||||||
|
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||||
|
|
||||||
|
KernelStaticLibrary hyperv_ic_heartbeat :
|
||||||
|
Heartbeat.cpp
|
||||||
|
HeartbeatModule.cpp
|
||||||
|
;
|
||||||
Reference in New Issue
Block a user