hyperv: Implement additional driver functions

* Switch instance ID from string to raw attribute
* Implement device allocate/free GPADL functions
* Add definitions and functions for additional VMBus packet types

Change-Id: I01e9a30f567358fad520df95b1c14b7ea9671915
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10402
Reviewed-by: Jérôme Duval <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
John Davis
2026-03-05 18:14:42 +00:00
committed by Jérôme Duval
parent a3fe30c940
commit 4d75e43574
8 changed files with 184 additions and 44 deletions
+9 -3
View File
@@ -15,7 +15,7 @@
// Device attributes for the VMBus device // Device attributes for the VMBus device
#define HYPERV_CHANNEL_ID_ITEM "hyperv/channel" #define HYPERV_CHANNEL_ID_ITEM "hyperv/channel"
#define HYPERV_DEVICE_TYPE_ITEM "hyperv/type" #define HYPERV_DEVICE_TYPE_ITEM "hyperv/type_string"
#define HYPERV_INSTANCE_ID_ITEM "hyperv/instance" #define HYPERV_INSTANCE_ID_ITEM "hyperv/instance"
#define HYPERV_PRETTYNAME_VMBUS "Hyper-V Virtual Machine Bus" #define HYPERV_PRETTYNAME_VMBUS "Hyper-V Virtual Machine Bus"
@@ -53,10 +53,16 @@ typedef struct hyperv_device_interface {
status_t (*open)(hyperv_device cookie, uint32 txLength, uint32 rxLength, status_t (*open)(hyperv_device cookie, uint32 txLength, uint32 rxLength,
hyperv_device_callback callback, void* callbackData); hyperv_device_callback callback, void* callbackData);
void (*close)(hyperv_device cookie); void (*close)(hyperv_device cookie);
status_t (*write_packet)(hyperv_device cookie, uint16 type, void* buffer,
uint32 length, bool responseRequired, uint64 transactionID);
status_t (*read_packet)(hyperv_device cookie, vmbus_pkt_header* _header, status_t (*read_packet)(hyperv_device cookie, vmbus_pkt_header* _header,
uint32* _headerLength, void* _buffer, uint32* _length); uint32* _headerLength, void* _buffer, uint32* _length);
status_t (*write_packet)(hyperv_device cookie, uint16 type, const void* buffer,
uint32 length, bool responseRequired, uint64 transactionID);
status_t (*write_gpa_packet)(hyperv_device cookie, uint32 rangeCount,
const vmbus_gpa_range* rangesList, uint32 rangesLength, const void* buffer,
uint32 length, bool responseRequired, uint64 transactionID);
status_t (*allocate_gpadl)(hyperv_device cookie, uint32 length, void** _buffer,
uint32* _gpadl);
status_t (*free_gpadl)(hyperv_device cookie, uint32 gpadl);
} hyperv_device_interface; } hyperv_device_interface;
+57 -6
View File
@@ -9,11 +9,14 @@
#include <SupportDefs.h> #include <SupportDefs.h>
#define HV_PAGE_SIZE 4096 #define HV_PAGE_SIZE 4096
#define HV_PAGE_SHIFT 12 #define HV_PAGE_MASK (HV_PAGE_SIZE - 1)
#define HV_PAGE_ALIGN(x) (((x) + (HV_PAGE_SIZE - 1)) & ~(HV_PAGE_SIZE - 1)) #define HV_PAGE_SHIFT 12
#define HV_BYTES_TO_PAGES(x) (HV_PAGE_ALIGN(x) >> HV_PAGE_SHIFT) #define HV_PAGE_ALIGN(x) (((x) + (HV_PAGE_SIZE - 1)) & ~(HV_PAGE_SIZE - 1))
#define HV_MS_TO_US(x) ((x) * 1000ULL) #define HV_BYTES_TO_PAGES(x) (HV_PAGE_ALIGN(x) >> HV_PAGE_SHIFT)
#define HV_BYTES_TO_SPAN_PAGES(a, l) ((((l) - 1) >> HV_PAGE_SHIFT) \
+ (((((l) - 1) & HV_PAGE_MASK) + ((a) & HV_PAGE_MASK)) >> HV_PAGE_SHIFT) + 1);
#define HV_MS_TO_US(x) ((x) * 1000ULL)
#define HV_STATIC_ASSERT(cond, msg) static_assert(cond, msg) #define HV_STATIC_ASSERT(cond, msg) static_assert(cond, msg)
@@ -56,6 +59,14 @@
#define VMBUS_TYPE_VSS "35fa2e29-ea23-4236-96ae-3a6ebacba440" #define VMBUS_TYPE_VSS "35fa2e29-ea23-4236-96ae-3a6ebacba440"
typedef struct {
uint32 data1;
uint16 data2;
uint16 data3;
uint8 data4[8];
} _PACKED vmbus_guid_t;
// VMBus packet types // VMBus packet types
enum { enum {
VMBUS_PKTTYPE_INVALID = 0, VMBUS_PKTTYPE_INVALID = 0,
@@ -89,7 +100,47 @@ typedef struct {
uint16 total_length; uint16 total_length;
uint16 flags; uint16 flags;
uint64 transaction_id; uint64 transaction_id;
} vmbus_pkt_header; } _PACKED vmbus_pkt_header;
// VMBus transfer range
// Describes a buffer within an existing defined buffer
typedef struct {
uint32 length;
uint32 offset;
} _PACKED vmbus_transfer_range;
// VMBus transfer ranges packet header
typedef struct {
vmbus_pkt_header header;
uint16 transfer_pageset_id;
uint8 sender_owns_sets;
uint8 reserved;
uint32 range_count;
vmbus_transfer_range ranges[];
} _PACKED vmbus_pkt_transfer_header;
// VMBus GPA (Guest Physical Address) range
// Describes a buffer made up of one or more physical pages in the guest
typedef struct {
uint32 length;
uint32 offset;
uint64 page_nums[];
} _PACKED vmbus_gpa_range;
// VMBus GPA packet header
typedef struct {
vmbus_pkt_header header;
uint32 reserved;
uint32 range_count;
vmbus_gpa_range ranges[];
} _PACKED vmbus_pkt_gpa_header;
#endif // _HYPERV_SPEC_H_ #endif // _HYPERV_SPEC_H_
@@ -1070,13 +1070,14 @@ status_t
VMBus::_RegisterChannel(VMBusChannel* channel) VMBus::_RegisterChannel(VMBusChannel* channel)
{ {
char typeStr[37]; char typeStr[37];
char instanceStr[37];
snprintf(typeStr, sizeof(typeStr), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", snprintf(typeStr, sizeof(typeStr), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
channel->type_id.data1, channel->type_id.data2, channel->type_id.data3, channel->type_id.data1, channel->type_id.data2, channel->type_id.data3,
channel->type_id.data4[0], channel->type_id.data4[1], channel->type_id.data4[2], channel->type_id.data4[0], channel->type_id.data4[1], channel->type_id.data4[2],
channel->type_id.data4[3], channel->type_id.data4[4], channel->type_id.data4[5], channel->type_id.data4[3], channel->type_id.data4[4], channel->type_id.data4[5],
channel->type_id.data4[6], channel->type_id.data4[7]); channel->type_id.data4[6], channel->type_id.data4[7]);
#ifdef TRACE_VMBUS
char instanceStr[37];
snprintf(instanceStr, sizeof(instanceStr), snprintf(instanceStr, sizeof(instanceStr),
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
channel->instance_id.data1, channel->instance_id.data2, channel->instance_id.data3, channel->instance_id.data1, channel->instance_id.data2, channel->instance_id.data3,
@@ -1086,6 +1087,7 @@ VMBus::_RegisterChannel(VMBusChannel* channel)
channel->instance_id.data4[6], channel->instance_id.data4[7]); channel->instance_id.data4[6], channel->instance_id.data4[7]);
TRACE("Registering VMBus channel %u type %s inst %s\n", channel->channel_id, typeStr, TRACE("Registering VMBus channel %u type %s inst %s\n", channel->channel_id, typeStr,
instanceStr); instanceStr);
#endif
char prettyName[sizeof(HYPERV_PRETTYNAME_VMBUS_DEVICE_FMT) + 8]; char prettyName[sizeof(HYPERV_PRETTYNAME_VMBUS_DEVICE_FMT) + 8];
snprintf(prettyName, sizeof(prettyName), HYPERV_PRETTYNAME_VMBUS_DEVICE_FMT, snprintf(prettyName, sizeof(prettyName), HYPERV_PRETTYNAME_VMBUS_DEVICE_FMT,
@@ -1100,8 +1102,8 @@ VMBus::_RegisterChannel(VMBusChannel* channel)
{ .ui32 = channel->channel_id }}, { .ui32 = channel->channel_id }},
{ HYPERV_DEVICE_TYPE_ITEM, B_STRING_TYPE, { HYPERV_DEVICE_TYPE_ITEM, B_STRING_TYPE,
{ .string = typeStr }}, { .string = typeStr }},
{ HYPERV_INSTANCE_ID_ITEM, B_STRING_TYPE, { HYPERV_INSTANCE_ID_ITEM, B_RAW_TYPE,
{ .string = instanceStr }}, { .raw = { .data = &channel->instance_id, .length = sizeof(channel->instance_id) }}},
{ NULL } { NULL }
}; };
@@ -81,8 +81,7 @@ VMBusDevice::Open(uint32 txLength, uint32 rxLength, hyperv_device_callback callb
TRACE("Open channel %u tx length 0x%X rx length 0x%X\n", fChannelID, txLength, rxLength); TRACE("Open channel %u tx length 0x%X rx length 0x%X\n", fChannelID, txLength, rxLength);
// Create the GPADL used for the ring buffers // Create the GPADL used for the ring buffers
status_t status = fVMBus->allocate_gpadl(fVMBusCookie, fChannelID, fRingBufferLength, status_t status = AllocateGPADL(fRingBufferLength, &fRingBuffer, &fRingGPADL);
&fRingBuffer, &fRingGPADL);
if (status != B_OK) { if (status != B_OK) {
ERROR("Failed to allocate GPADL while opening channel %u (%s)\n", fChannelID, ERROR("Failed to allocate GPADL while opening channel %u (%s)\n", fChannelID,
strerror(status)); strerror(status));
@@ -133,7 +132,7 @@ VMBusDevice::Close()
if (status != B_OK) if (status != B_OK)
ERROR("Failed to close channel %u (%s)\n", fChannelID, strerror(status)); ERROR("Failed to close channel %u (%s)\n", fChannelID, strerror(status));
status = fVMBus->free_gpadl(fVMBusCookie, fChannelID, fRingGPADL); status = FreeGPADL(fRingGPADL);
if (status != B_OK) if (status != B_OK)
ERROR("Failed to free ring GPADL for channel %u (%s)\n", fChannelID, strerror(status)); ERROR("Failed to free ring GPADL for channel %u (%s)\n", fChannelID, strerror(status));
@@ -148,8 +147,8 @@ status_t
VMBusDevice::WritePacket(uint16 type, const void* buffer, uint32 length, bool responseRequired, VMBusDevice::WritePacket(uint16 type, const void* buffer, uint32 length, bool responseRequired,
uint64 transactionID) uint64 transactionID)
{ {
TRACE_TX("Channel %u TX pkt %u len 0x%X resp %u tran %lu\n", fChannelID, type, length, TRACE_TX("Channel %u TX pkt %u len 0x%X resp %u tran %" B_PRIu64 "\n", fChannelID, type,
responseRequired, transactionID); length, responseRequired, transactionID);
vmbus_pkt_header header; vmbus_pkt_header header;
uint32 totalLength = sizeof(header) + length; uint32 totalLength = sizeof(header) + length;
@@ -161,16 +160,53 @@ VMBusDevice::WritePacket(uint16 type, const void* buffer, uint32 length, bool re
header.flags = responseRequired ? VMBUS_PKT_FLAGS_RESPONSE_REQUIRED : 0; header.flags = responseRequired ? VMBUS_PKT_FLAGS_RESPONSE_REQUIRED : 0;
header.transaction_id = transactionID; header.transaction_id = transactionID;
iovec pkt[3]; iovec data[3];
uint64 padding = 0; uint64 padding = 0;
pkt[0].iov_base = &header; data[0].iov_base = &header;
pkt[0].iov_len = sizeof(header); data[0].iov_len = sizeof(header);
pkt[1].iov_base = (void*)buffer; data[1].iov_base = (void*)buffer;
pkt[1].iov_len = length; data[1].iov_len = length;
pkt[2].iov_base = &padding; data[2].iov_base = &padding;
pkt[2].iov_len = totalLengthAligned - totalLength; data[2].iov_len = totalLengthAligned - totalLength;
return _WriteTXData(pkt, 3); return _WriteTXData(data, 3);
}
status_t
VMBusDevice::WriteGPAPacket(uint32 rangeCount, const vmbus_gpa_range* rangesList,
uint32 rangesLength, const void* buffer, uint32 length, bool responseRequired,
uint64 transactionID)
{
TRACE_TX("Channel %u TX gpa pkt cnt %u gpa len 0x%X len 0x%X resp %u tran %" B_PRIu64 "\n",
fChannelID, rangeCount, rangesLength, length, responseRequired, transactionID);
vmbus_pkt_gpa_header gpa;
uint32 headerLength = sizeof(gpa) + rangesLength;
uint32 totalLength = headerLength + length;
uint32 totalLengthAligned = VMBUS_PKT_ALIGN(totalLength);
gpa.header.type = VMBUS_PKTTYPE_DATA_USING_GPA_DIRECT;
gpa.header.header_length = static_cast<uint16>(headerLength >> VMBUS_PKT_SIZE_SHIFT);
gpa.header.total_length = static_cast<uint16>(totalLengthAligned >> VMBUS_PKT_SIZE_SHIFT);
gpa.header.flags = responseRequired ? VMBUS_PKT_FLAGS_RESPONSE_REQUIRED : 0;
gpa.header.transaction_id = transactionID;
gpa.reserved = 0;
gpa.range_count = rangeCount;
iovec data[4];
uint64 padding = 0;
data[0].iov_base = &gpa;
data[0].iov_len = sizeof(gpa);
data[1].iov_base = (void*)rangesList;
data[1].iov_len = rangesLength;
data[2].iov_base = (void*)buffer;
data[2].iov_len = length;
data[3].iov_base = &padding;
data[3].iov_len = totalLengthAligned - totalLength;
return _WriteTXData(data, 4);
} }
@@ -263,6 +299,20 @@ VMBusDevice::ReadPacket(vmbus_pkt_header* _header, uint32* _headerLength, void*
} }
status_t
VMBusDevice::AllocateGPADL(uint32 length, void** _buffer, uint32* _gpadl)
{
return fVMBus->allocate_gpadl(fVMBusCookie, fChannelID, length, _buffer, _gpadl);
}
status_t
VMBusDevice::FreeGPADL(uint32 gpadl)
{
return fVMBus->free_gpadl(fVMBusCookie, fChannelID, gpadl);
}
/*static*/ void /*static*/ void
VMBusDevice::_CallbackHandler(void* arg) VMBusDevice::_CallbackHandler(void* arg)
{ {
@@ -76,7 +76,16 @@ vmbus_device_close(hyperv_device cookie)
static status_t static status_t
vmbus_device_write_packet(hyperv_device cookie, uint16 type, void* buffer, uint32 length, vmbus_device_read_packet(hyperv_device cookie, vmbus_pkt_header* _header, uint32* _headerLength,
void* _buffer, uint32* _length)
{
VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie);
return device->ReadPacket(_header, _headerLength, _buffer, _length);
}
static status_t
vmbus_device_write_packet(hyperv_device cookie, uint16 type, const void* buffer, uint32 length,
bool responseRequired, uint64 transactionID) bool responseRequired, uint64 transactionID)
{ {
VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie); VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie);
@@ -85,11 +94,29 @@ vmbus_device_write_packet(hyperv_device cookie, uint16 type, void* buffer, uint3
static status_t static status_t
vmbus_device_read_packet(hyperv_device cookie, vmbus_pkt_header* _header, uint32* _headerLength, vmbus_device_write_gpa_packet(hyperv_device cookie, uint32 rangeCount,
void* _buffer, uint32* _length) const vmbus_gpa_range* rangesList, uint32 rangesLength, const void* buffer, uint32 length,
bool responseRequired, uint64 transactionID)
{ {
VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie); VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie);
return device->ReadPacket(_header, _headerLength, _buffer, _length); return device->WriteGPAPacket(rangeCount, rangesList, rangesLength, buffer, length,
responseRequired, transactionID);
}
static status_t
vmbus_device_allocate_gpadl(hyperv_device cookie, uint32 length, void** _buffer, uint32* _gpadl)
{
VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie);
return device->AllocateGPADL(length, _buffer, _gpadl);
}
static status_t
vmbus_device_free_gpadl(hyperv_device cookie, uint32 gpadl)
{
VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie);
return device->FreeGPADL(gpadl);
} }
@@ -128,6 +155,9 @@ hyperv_device_interface gVMBusDeviceModule = {
vmbus_device_get_bus_version, vmbus_device_get_bus_version,
vmbus_device_open, vmbus_device_open,
vmbus_device_close, vmbus_device_close,
vmbus_device_read_packet,
vmbus_device_write_packet, vmbus_device_write_packet,
vmbus_device_read_packet vmbus_device_write_gpa_packet,
vmbus_device_allocate_gpadl,
vmbus_device_free_gpadl
}; };
@@ -50,12 +50,20 @@ public:
status_t Open(uint32 txLength, uint32 rxLength, status_t Open(uint32 txLength, uint32 rxLength,
hyperv_device_callback callback, void* callbackData); hyperv_device_callback callback, void* callbackData);
void Close(); void Close();
status_t WritePacket(uint16 type, const void* buffer, uint32 length, status_t WritePacket(uint16 type, const void* buffer, uint32 length,
bool responseRequired, uint64 transactionID); bool responseRequired, uint64 transactionID);
status_t WriteGPAPacket(uint32 rangeCount,
const vmbus_gpa_range* rangesList, uint32 rangesLength,
const void* buffer, uint32 length, bool responseRequired,
uint64 transactionID);
status_t PeekPacket(void* _buffer, uint32 length); status_t PeekPacket(void* _buffer, uint32 length);
status_t ReadPacket(vmbus_pkt_header* _header, uint32* _headerLength, status_t ReadPacket(vmbus_pkt_header* _header, uint32* _headerLength,
void* _buffer, uint32* _length); void* _buffer, uint32* _length);
status_t AllocateGPADL(uint32 length, void** _buffer, uint32* _gpadl);
status_t FreeGPADL(uint32 gpadl);
private: private:
static void _CallbackHandler(void* arg); static void _CallbackHandler(void* arg);
static void _DPCHandler(void* arg); static void _DPCHandler(void* arg);
@@ -116,6 +116,7 @@ public:
uint32 GetVersion() const { return fVersion; } uint32 GetVersion() const { return fVersion; }
status_t RequestChannels(); status_t RequestChannels();
status_t OpenChannel(uint32 channelID, uint32 gpadlID, uint32 rxOffset, status_t OpenChannel(uint32 channelID, uint32 gpadlID, uint32 rxOffset,
hyperv_bus_callback callback, void* callbackData); hyperv_bus_callback callback, void* callbackData);
status_t CloseChannel(uint32 channelID); status_t CloseChannel(uint32 channelID);
@@ -167,14 +167,6 @@ static const uint32 vmbus_versions[] = {
}; };
typedef struct {
uint32 data1;
uint16 data2;
uint16 data3;
uint8 data4[8];
} _PACKED vmbus_guid_t;
// VMBus GPADL range descriptor // VMBus GPADL range descriptor
typedef struct { typedef struct {
uint32 length; uint32 length;
@@ -321,11 +313,11 @@ typedef struct {
typedef struct { typedef struct {
vmbus_msg_header header; vmbus_msg_header header;
uint32 channel_id; uint32 channel_id;
uint32 gpadl_id; uint32 gpadl_id;
uint16 total_range_length; uint16 total_range_length;
uint16 range_count; uint16 range_count;
vmbus_gpadl_range ranges[1]; // Only 1 range is supported by this driver vmbus_gpa_range ranges[1]; // Only 1 range is supported by this driver
} _PACKED vmbus_msg_create_gpadl; } _PACKED vmbus_msg_create_gpadl;
#define VMBUS_MSG_CREATE_GPADL_MAX_PAGES \ #define VMBUS_MSG_CREATE_GPADL_MAX_PAGES \
((HYPERCALL_MAX_DATA_SIZE - sizeof(vmbus_msg_create_gpadl)) / sizeof(uint64)) ((HYPERCALL_MAX_DATA_SIZE - sizeof(vmbus_msg_create_gpadl)) / sizeof(uint64))