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 dvb ;
|
||||
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 joystick ;
|
||||
EnableWerror src add-ons kernel drivers midi ;
|
||||
|
||||
@@ -154,6 +154,9 @@ AddNewDriversToPackage display :
|
||||
AddNewDriversToPackage audio hmulti :
|
||||
# virtio_sound
|
||||
;
|
||||
AddNewDriversToPackage hyperv :
|
||||
hyperv_ic@x86,x86_64
|
||||
;
|
||||
|
||||
# legacy drivers
|
||||
AddDriversToPackage :
|
||||
|
||||
@@ -99,6 +99,9 @@ AddNewDriversToPackage network :
|
||||
AddNewDriversToPackage display :
|
||||
# display_adapter@x86
|
||||
;
|
||||
AddNewDriversToPackage hyperv :
|
||||
hyperv_ic@x86,x86_64
|
||||
;
|
||||
|
||||
# legacy drivers
|
||||
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 display ;
|
||||
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 joystick ;
|
||||
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