2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
40 #include <ipxe/malloc.h>
41 #include <ipxe/device.h>
42 #include <ipxe/cpuid.h>
44 #include <ipxe/hyperv.h>
45 #include <ipxe/vmbus.h>
48 /** Maximum time to wait for a message response
50 * This is a policy decision.
52 #define HV_MESSAGE_MAX_WAIT_MS 1000
55 * Convert a Hyper-V status code to an iPXE status code
57 * @v status Hyper-V status code
58 * @ret rc iPXE status code (before negation)
60 #define EHV( status ) EPLATFORM ( EINFO_EPLATFORM, (status) )
63 * Allocate zeroed pages
65 * @v hv Hyper-V hypervisor
66 * @v ... Page addresses to fill in, terminated by NULL
67 * @ret rc Return status code
69 __attribute__ (( sentinel )) int
70 hv_alloc_pages ( struct hv_hypervisor *hv, ... ) {
75 /* Allocate and zero pages */
76 va_start ( args, hv );
77 for ( i = 0 ; ( ( page = va_arg ( args, void ** ) ) != NULL ); i++ ) {
78 *page = malloc_dma ( PAGE_SIZE, PAGE_SIZE );
81 memset ( *page, 0, PAGE_SIZE );
89 va_start ( args, hv );
90 for ( ; i >= 0 ; i-- ) {
91 page = va_arg ( args, void ** );
92 free_dma ( *page, PAGE_SIZE );
101 * @v hv Hyper-V hypervisor
102 * @v ... Page addresses, terminated by NULL
104 __attribute__ (( sentinel )) void
105 hv_free_pages ( struct hv_hypervisor *hv, ... ) {
109 va_start ( args, hv );
110 while ( ( page = va_arg ( args, void * ) ) != NULL )
111 free_dma ( page, PAGE_SIZE );
116 * Allocate message buffer
118 * @v hv Hyper-V hypervisor
119 * @ret rc Return status code
121 static int hv_alloc_message ( struct hv_hypervisor *hv ) {
123 /* Allocate buffer. Must be aligned to at least 8 bytes and
124 * must not cross a page boundary, so align on its own size.
126 hv->message = malloc_dma ( sizeof ( *hv->message ),
127 sizeof ( *hv->message ) );
135 * Free message buffer
137 * @v hv Hyper-V hypervisor
139 static void hv_free_message ( struct hv_hypervisor *hv ) {
142 free_dma ( hv->message, sizeof ( *hv->message ) );
146 * Check whether or not we are running in Hyper-V
148 * @v hv Hyper-V hypervisor
149 * @ret rc Return status code
151 static int hv_check_hv ( struct hv_hypervisor *hv ) {
152 struct x86_features features;
153 uint32_t interface_id;
154 uint32_t discard_ebx;
155 uint32_t discard_ecx;
156 uint32_t discard_edx;
158 uint32_t permissions;
160 /* Check for presence of a hypervisor (not necessarily Hyper-V) */
161 x86_features ( &features );
162 if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) {
163 DBGC ( hv, "HV %p not running in a hypervisor\n", hv );
167 /* Check that hypervisor is Hyper-V */
168 cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
169 &discard_ecx, &discard_edx );
170 if ( interface_id != HV_INTERFACE_ID ) {
171 DBGC ( hv, "HV %p not running in Hyper-V (interface ID "
172 "%#08x)\n", hv, interface_id );
176 /* Check that required features and privileges are available */
177 cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx,
179 if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
180 DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
181 hv, available, permissions );
184 if ( ! ( available & HV_FEATURES_AVAIL_SYNIC_MSR ) ) {
185 DBGC ( hv, "HV %p has no SynIC MSRs (features %08x:%08x)\n",
186 hv, available, permissions );
189 if ( ! ( permissions & HV_FEATURES_PERM_POST_MESSAGES ) ) {
190 DBGC ( hv, "HV %p cannot post messages (features %08x:%08x)\n",
191 hv, available, permissions );
194 if ( ! ( permissions & HV_FEATURES_PERM_SIGNAL_EVENTS ) ) {
195 DBGC ( hv, "HV %p cannot signal events (features %08x:%08x)",
196 hv, available, permissions );
206 * @v hv Hyper-V hypervisor
207 * @ret rc Return status code
209 static int hv_map_hypercall ( struct hv_hypervisor *hv ) {
215 } __attribute__ (( packed ));
216 char text[ 13 /* "bbbbccccdddd" + NUL */ ];
220 uint32_t discard_eax;
221 uint32_t discard_ecx;
222 uint32_t discard_edx;
223 uint64_t guest_os_id;
226 /* Report guest OS identity */
227 guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
228 if ( guest_os_id != 0 ) {
229 DBGC ( hv, "HV %p guest OS ID MSR already set to %#08llx\n",
233 guest_os_id = HV_GUEST_OS_ID_IPXE;
234 DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
235 wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
237 /* Get hypervisor system identity (for debugging) */
238 cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx,
239 &vendor_id.ecx, &vendor_id.edx );
240 vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
241 cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx,
243 DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
244 vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
246 /* Map hypercall page */
247 hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
248 hypercall &= ( PAGE_SIZE - 1 );
249 hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE );
250 DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
251 wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
257 * Unmap hypercall page
259 * @v hv Hyper-V hypervisor
261 static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) {
263 uint64_t guest_os_id;
265 /* Unmap the hypercall page */
266 hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
267 hypercall &= ( ( PAGE_SIZE - 1 ) & ~HV_HYPERCALL_ENABLE );
268 DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
269 wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
271 /* Reset the guest OS identity */
273 DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
274 wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
278 * Map synthetic interrupt controller
280 * @v hv Hyper-V hypervisor
281 * @ret rc Return status code
283 static int hv_map_synic ( struct hv_hypervisor *hv ) {
288 /* Map SynIC message page */
289 simp = rdmsr ( HV_X64_MSR_SIMP );
290 simp &= ( PAGE_SIZE - 1 );
291 simp |= ( virt_to_phys ( hv->synic.message ) | HV_SIMP_ENABLE );
292 DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
293 wrmsr ( HV_X64_MSR_SIMP, simp );
295 /* Map SynIC event page */
296 siefp = rdmsr ( HV_X64_MSR_SIEFP );
297 siefp &= ( PAGE_SIZE - 1 );
298 siefp |= ( virt_to_phys ( hv->synic.event ) | HV_SIEFP_ENABLE );
299 DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
300 wrmsr ( HV_X64_MSR_SIEFP, siefp );
303 scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
304 scontrol |= HV_SCONTROL_ENABLE;
305 DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
306 wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
312 * Unmap synthetic interrupt controller
314 * @v hv Hyper-V hypervisor
316 static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
322 scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
323 scontrol &= ~HV_SCONTROL_ENABLE;
324 DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
325 wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
327 /* Unmap SynIC event page */
328 siefp = rdmsr ( HV_X64_MSR_SIEFP );
329 siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
330 DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
331 wrmsr ( HV_X64_MSR_SIEFP, siefp );
333 /* Unmap SynIC message page */
334 simp = rdmsr ( HV_X64_MSR_SIMP );
335 simp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIMP_ENABLE );
336 DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
337 wrmsr ( HV_X64_MSR_SIMP, simp );
341 * Enable synthetic interrupt
343 * @v hv Hyper-V hypervisor
344 * @v sintx Synthetic interrupt number
346 void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
347 unsigned long msr = HV_X64_MSR_SINT ( sintx );
350 /* Enable synthetic interrupt
352 * We have to enable the interrupt, otherwise messages will
353 * not be delivered (even though the documentation implies
354 * that polling for messages is possible). We enable AutoEOI
355 * and hook the interrupt to the obsolete IRQ13 (FPU
356 * exception) vector, which will be implemented as a no-op.
358 sint = rdmsr ( msr );
359 sint &= ~( HV_SINT_MASKED | HV_SINT_VECTOR_MASK );
360 sint |= ( HV_SINT_AUTO_EOI |
361 HV_SINT_VECTOR ( IRQ_INT ( 13 /* See comment above */ ) ) );
362 DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
367 * Disable synthetic interrupt
369 * @v hv Hyper-V hypervisor
370 * @v sintx Synthetic interrupt number
372 void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
373 unsigned long msr = HV_X64_MSR_SINT ( sintx );
376 /* Disable synthetic interrupt */
377 sint = rdmsr ( msr );
378 sint &= ~HV_SINT_AUTO_EOI;
379 sint |= HV_SINT_MASKED;
380 DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
387 * @v hv Hyper-V hypervisor
388 * @v id Connection ID
389 * @v type Message type
391 * @v len Length of message
392 * @ret rc Return status code
394 int hv_post_message ( struct hv_hypervisor *hv, unsigned int id,
395 unsigned int type, const void *data, size_t len ) {
396 struct hv_post_message *msg = &hv->message->posted;
401 assert ( len <= sizeof ( msg->data ) );
403 /* Construct message */
404 memset ( msg, 0, sizeof ( *msg ) );
405 msg->id = cpu_to_le32 ( id );
406 msg->type = cpu_to_le32 ( type );
407 msg->len = cpu_to_le32 ( len );
408 memcpy ( msg->data, data, len );
409 DBGC2 ( hv, "HV %p connection %d posting message type %#08x:\n",
411 DBGC2_HDA ( hv, 0, msg->data, len );
414 if ( ( status = hv_call ( hv, HV_POST_MESSAGE, msg, NULL ) ) != 0 ) {
415 rc = -EHV ( status );
416 DBGC ( hv, "HV %p could not post message to %#08x: %s\n",
417 hv, id, strerror ( rc ) );
425 * Wait for received message
427 * @v hv Hyper-V hypervisor
428 * @v sintx Synthetic interrupt number
429 * @ret rc Return status code
431 int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ) {
432 struct hv_message *msg = &hv->message->received;
433 struct hv_message *src = &hv->synic.message[sintx];
434 unsigned int retries;
437 /* Wait for message to arrive */
438 for ( retries = 0 ; retries < HV_MESSAGE_MAX_WAIT_MS ; retries++ ) {
440 /* Check for message */
444 memset ( msg, 0, sizeof ( *msg ) );
446 assert ( len <= sizeof ( *msg ) );
448 ( offsetof ( typeof ( *msg ), data ) + len ) );
449 DBGC2 ( hv, "HV %p SINT%d received message type "
450 "%#08x:\n", hv, sintx,
451 le32_to_cpu ( msg->type ) );
452 DBGC2_HDA ( hv, 0, msg->data, len );
454 /* Consume message */
460 /* Trigger message delivery */
461 wrmsr ( HV_X64_MSR_EOM, 0 );
467 DBGC ( hv, "HV %p SINT%d timed out waiting for message\n",
475 * @v hv Hyper-V hypervisor
476 * @v id Connection ID
477 * @v flag Flag number
478 * @ret rc Return status code
480 int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id,
481 unsigned int flag ) {
482 struct hv_signal_event *event = &hv->message->signalled;
486 /* Construct event */
487 memset ( event, 0, sizeof ( *event ) );
488 event->id = cpu_to_le32 ( id );
489 event->flag = cpu_to_le16 ( flag );
492 if ( ( status = hv_call ( hv, HV_SIGNAL_EVENT, event, NULL ) ) != 0 ) {
493 rc = -EHV ( status );
494 DBGC ( hv, "HV %p could not signal event to %#08x: %s\n",
495 hv, id, strerror ( rc ) );
505 * @v rootdev Root device
506 * @ret rc Return status code
508 static int hv_probe ( struct root_device *rootdev ) {
509 struct hv_hypervisor *hv;
512 /* Allocate and initialise structure */
513 hv = zalloc ( sizeof ( *hv ) );
519 /* Check we are running in Hyper-V */
520 if ( ( rc = hv_check_hv ( hv ) ) != 0 )
524 if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
525 &hv->synic.event, NULL ) ) != 0 )
526 goto err_alloc_pages;
528 /* Allocate message buffer */
529 if ( ( rc = hv_alloc_message ( hv ) ) != 0 )
530 goto err_alloc_message;
532 /* Map hypercall page */
533 if ( ( rc = hv_map_hypercall ( hv ) ) != 0 )
534 goto err_map_hypercall;
536 /* Map synthetic interrupt controller */
537 if ( ( rc = hv_map_synic ( hv ) ) != 0 )
540 /* Probe Hyper-V devices */
541 if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 )
542 goto err_vmbus_probe;
544 rootdev_set_drvdata ( rootdev, hv );
547 vmbus_remove ( hv, &rootdev->dev );
549 hv_unmap_synic ( hv );
551 hv_unmap_hypercall ( hv );
553 hv_free_message ( hv );
555 hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
567 * @v rootdev Root device
569 static void hv_remove ( struct root_device *rootdev ) {
570 struct hv_hypervisor *hv = rootdev_get_drvdata ( rootdev );
572 vmbus_remove ( hv, &rootdev->dev );
573 hv_unmap_synic ( hv );
574 hv_unmap_hypercall ( hv );
575 hv_free_message ( hv );
576 hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
581 /** Hyper-V root device driver */
582 static struct root_driver hv_root_driver = {
587 /** Hyper-V root device */
588 struct root_device hv_root_device __root_device = {
589 .dev = { .name = "Hyper-V" },
590 .driver = &hv_root_driver,
593 /* Drag in objects via hv_root_device */
594 REQUIRING_SYMBOL ( hv_root_device );
596 /* Drag in netvsc driver */
597 REQUIRE_OBJECT ( netvsc );