Преглед на файлове

[hyperv] Add support for Hyper-V hypervisor

Add support for detecting and communicating with the Hyper-V
hypervisor.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown преди 9 години
родител
ревизия
d77a546fb4

+ 72
- 0
src/arch/i386/include/bits/hyperv.h Целия файл

@@ -0,0 +1,72 @@
1
+#ifndef _BITS_HYPERV_H
2
+#define _BITS_HYPERV_H
3
+
4
+/** @file
5
+ *
6
+ * Hyper-V interface
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stddef.h>
13
+#include <stdint.h>
14
+#include <ipxe/io.h>
15
+
16
+/**
17
+ * Issue hypercall
18
+ *
19
+ * @v hv		Hyper-V hypervisor
20
+ * @v code		Call code
21
+ * @v in		Input parameters
22
+ * @v out		Output parameters
23
+ * @ret status		Status code
24
+ */
25
+static inline __attribute__ (( always_inline )) int
26
+hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in,
27
+	  void *out ) {
28
+	void *hypercall = hv->hypercall;
29
+	uint32_t in_phys;
30
+	uint32_t out_phys;
31
+	uint32_t discard_ecx;
32
+	uint32_t discard_edx;
33
+	uint16_t result;
34
+
35
+	in_phys = ( ( __builtin_constant_p ( in ) && ( in == NULL ) )
36
+		       ? 0 : virt_to_phys ( in ) );
37
+	out_phys = ( ( __builtin_constant_p ( out ) && ( out == NULL ) )
38
+		       ? 0 : virt_to_phys ( out ) );
39
+	__asm__ __volatile__ ( "call *%9"
40
+			       : "=a" ( result ), "=c" ( discard_ecx ),
41
+				 "=d" ( discard_edx )
42
+			       : "d" ( 0 ), "a" ( code ),
43
+				 "b" ( 0 ), "c" ( in_phys ),
44
+				 "D" ( 0 ), "S" ( out_phys ),
45
+				 "m" ( hypercall ) );
46
+	return result;
47
+}
48
+
49
+/**
50
+ * Set bit atomically
51
+ *
52
+ * @v bits		Bit field
53
+ * @v bit		Bit to set
54
+ */
55
+static inline __attribute__ (( always_inline )) void
56
+hv_set_bit ( void *bits, unsigned int bit ) {
57
+	struct {
58
+		uint32_t dword[ ( bit / 32 ) + 1 ];
59
+	} *dwords = bits;
60
+
61
+	/* Set bit using "lock bts".  Inform compiler that any memory
62
+	 * from the start of the bit field up to and including the
63
+	 * dword containing this bit may be modified.  (This is
64
+	 * overkill but shouldn't matter in practice since we're
65
+	 * unlikely to subsequently read other bits from the same bit
66
+	 * field.)
67
+	 */
68
+	__asm__ __volatile__ ( "lock bts %1, %0"
69
+			       : "+m" ( *dwords ) : "Ir" ( bit ) );
70
+}
71
+
72
+#endif /* _BITS_HYPERV_H */

+ 1
- 0
src/arch/x86/Makefile Целия файл

@@ -9,6 +9,7 @@ SRCDIRS		+= arch/x86/interface/efi
9 9
 SRCDIRS		+= arch/x86/prefix
10 10
 SRCDIRS		+= arch/x86/hci/commands
11 11
 SRCDIRS		+= arch/x86/drivers/xen
12
+SRCDIRS		+= arch/x86/drivers/hyperv
12 13
 
13 14
 # breaks building some of the linux-related objects
14 15
 CFLAGS		+= -Ulinux

src/arch/i386/core/pic8259.c → src/arch/x86/core/pic8259.c Целия файл


+ 553
- 0
src/arch/x86/drivers/hyperv/hyperv.c Целия файл

@@ -0,0 +1,553 @@
1
+/*
2
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
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.
8
+ *
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.
13
+ *
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
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+/** @file
23
+ *
24
+ * Hyper-V driver
25
+ *
26
+ */
27
+
28
+#include <stdlib.h>
29
+#include <stdarg.h>
30
+#include <string.h>
31
+#include <unistd.h>
32
+#include <assert.h>
33
+#include <errno.h>
34
+#include <byteswap.h>
35
+#include <pic8259.h>
36
+#include <ipxe/malloc.h>
37
+#include <ipxe/device.h>
38
+#include <ipxe/cpuid.h>
39
+#include <ipxe/msr.h>
40
+#include <ipxe/hyperv.h>
41
+#include "hyperv.h"
42
+
43
+/** Maximum time to wait for a message response
44
+ *
45
+ * This is a policy decision.
46
+ */
47
+#define HV_MESSAGE_MAX_WAIT_MS 1000
48
+
49
+/**
50
+ * Convert a Hyper-V status code to an iPXE status code
51
+ *
52
+ * @v status		Hyper-V status code
53
+ * @ret rc		iPXE status code (before negation)
54
+ */
55
+#define EHV( status ) EPLATFORM ( EINFO_EPLATFORM, (status) )
56
+
57
+/**
58
+ * Allocate zeroed pages
59
+ *
60
+ * @v hv		Hyper-V hypervisor
61
+ * @v ...		Page addresses to fill in, terminated by NULL
62
+ * @ret rc		Return status code
63
+ */
64
+__attribute__ (( sentinel )) int
65
+hv_alloc_pages ( struct hv_hypervisor *hv, ... ) {
66
+	va_list args;
67
+	void **page;
68
+	int i;
69
+
70
+	/* Allocate and zero pages */
71
+	va_start ( args, hv );
72
+	for ( i = 0 ; ( ( page = va_arg ( args, void ** ) ) != NULL ); i++ ) {
73
+		*page = malloc_dma ( PAGE_SIZE, PAGE_SIZE );
74
+		if ( ! *page )
75
+			goto err_alloc;
76
+		memset ( *page, 0, PAGE_SIZE );
77
+	}
78
+	va_end ( args );
79
+
80
+	return 0;
81
+
82
+ err_alloc:
83
+	va_end ( args );
84
+	va_start ( args, hv );
85
+	for ( ; i >= 0 ; i-- ) {
86
+		page = va_arg ( args, void ** );
87
+		free_dma ( *page, PAGE_SIZE );
88
+	}
89
+	va_end ( args );
90
+	return -ENOMEM;
91
+}
92
+
93
+/**
94
+ * Free pages
95
+ *
96
+ * @v hv		Hyper-V hypervisor
97
+ * @v ...		Page addresses, terminated by NULL
98
+ */
99
+__attribute__ (( sentinel )) void
100
+hv_free_pages ( struct hv_hypervisor *hv, ... ) {
101
+	va_list args;
102
+	void *page;
103
+
104
+	va_start ( args, hv );
105
+	while ( ( page = va_arg ( args, void * ) ) != NULL )
106
+		free_dma ( page, PAGE_SIZE );
107
+	va_end ( args );
108
+}
109
+
110
+/**
111
+ * Allocate message buffer
112
+ *
113
+ * @v hv		Hyper-V hypervisor
114
+ * @ret rc		Return status code
115
+ */
116
+static int hv_alloc_message ( struct hv_hypervisor *hv ) {
117
+
118
+	/* Allocate buffer.  Must be aligned to at least 8 bytes and
119
+	 * must not cross a page boundary, so align on its own size.
120
+	 */
121
+	hv->message = malloc_dma ( sizeof ( *hv->message ),
122
+				   sizeof ( *hv->message ) );
123
+	if ( ! hv->message )
124
+		return -ENOMEM;
125
+
126
+	return 0;
127
+}
128
+
129
+/**
130
+ * Free message buffer
131
+ *
132
+ * @v hv		Hyper-V hypervisor
133
+ */
134
+static void hv_free_message ( struct hv_hypervisor *hv ) {
135
+
136
+	/* Free buffer */
137
+	free_dma ( hv->message, sizeof ( *hv->message ) );
138
+}
139
+
140
+/**
141
+ * Check whether or not we are running in Hyper-V
142
+ *
143
+ * @v hv		Hyper-V hypervisor
144
+ * @ret rc		Return status code
145
+ */
146
+static int hv_check_hv ( struct hv_hypervisor *hv ) {
147
+	struct x86_features features;
148
+	uint32_t interface_id;
149
+	uint32_t discard_ebx;
150
+	uint32_t discard_ecx;
151
+	uint32_t discard_edx;
152
+
153
+	/* Check for presence of a hypervisor (not necessarily Hyper-V) */
154
+	x86_features ( &features );
155
+	if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) {
156
+		DBGC ( hv, "HV %p not running in a hypervisor\n", hv );
157
+		return -ENODEV;
158
+	}
159
+
160
+	/* Check that hypervisor is Hyper-V */
161
+	cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx,
162
+		&discard_ecx, &discard_edx );
163
+	if ( interface_id != HV_INTERFACE_ID ) {
164
+		DBGC ( hv, "HV %p not running in Hyper-V (interface ID "
165
+		       "%#08x)\n", hv, interface_id );
166
+		return -ENODEV;
167
+	}
168
+
169
+	return 0;
170
+}
171
+
172
+/**
173
+ * Map hypercall page
174
+ *
175
+ * @v hv		Hyper-V hypervisor
176
+ * @ret rc		Return status code
177
+ */
178
+static int hv_map_hypercall ( struct hv_hypervisor *hv ) {
179
+	union {
180
+		struct {
181
+			uint32_t ebx;
182
+			uint32_t ecx;
183
+			uint32_t edx;
184
+		} __attribute__ (( packed ));
185
+		char text[ 13 /* "bbbbccccdddd" + NUL */ ];
186
+	} vendor_id;
187
+	uint32_t build;
188
+	uint32_t version;
189
+	uint32_t discard_eax;
190
+	uint32_t discard_ecx;
191
+	uint32_t discard_edx;
192
+	uint64_t guest_os_id;
193
+	uint64_t hypercall;
194
+
195
+	/* Report guest OS identity */
196
+	guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
197
+	if ( guest_os_id != 0 ) {
198
+		DBGC ( hv, "HV %p guest OS ID MSR already set to %#08llx\n",
199
+		       hv, guest_os_id );
200
+		return -EBUSY;
201
+	}
202
+	guest_os_id = HV_GUEST_OS_ID_IPXE;
203
+	DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
204
+	wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
205
+
206
+	/* Get hypervisor system identity (for debugging) */
207
+	cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx,
208
+		&vendor_id.ecx, &vendor_id.edx );
209
+	vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
210
+	cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx,
211
+		&discard_edx );
212
+	DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
213
+	       vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
214
+
215
+	/* Map hypercall page */
216
+	hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
217
+	hypercall &= ( PAGE_SIZE - 1 );
218
+	hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE );
219
+	DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
220
+	wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
221
+
222
+	return 0;
223
+}
224
+
225
+/**
226
+ * Unmap hypercall page
227
+ *
228
+ * @v hv		Hyper-V hypervisor
229
+ */
230
+static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) {
231
+	uint64_t hypercall;
232
+	uint64_t guest_os_id;
233
+
234
+	/* Unmap the hypercall page */
235
+	hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
236
+	hypercall &= ( ( PAGE_SIZE - 1 ) & ~HV_HYPERCALL_ENABLE );
237
+	DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
238
+	wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
239
+
240
+	/* Reset the guest OS identity */
241
+	guest_os_id = 0;
242
+	DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
243
+	wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
244
+}
245
+
246
+/**
247
+ * Map synthetic interrupt controller
248
+ *
249
+ * @v hv		Hyper-V hypervisor
250
+ * @ret rc		Return status code
251
+ */
252
+static int hv_map_synic ( struct hv_hypervisor *hv ) {
253
+	uint64_t simp;
254
+	uint64_t siefp;
255
+	uint64_t scontrol;
256
+
257
+	/* Map SynIC message page */
258
+	simp = rdmsr ( HV_X64_MSR_SIMP );
259
+	simp &= ( PAGE_SIZE - 1 );
260
+	simp |= ( virt_to_phys ( hv->synic.message ) | HV_SIMP_ENABLE );
261
+	DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
262
+	wrmsr ( HV_X64_MSR_SIMP, simp );
263
+
264
+	/* Map SynIC event page */
265
+	siefp = rdmsr ( HV_X64_MSR_SIEFP );
266
+	siefp &= ( PAGE_SIZE - 1 );
267
+	siefp |= ( virt_to_phys ( hv->synic.event ) | HV_SIEFP_ENABLE );
268
+	DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
269
+	wrmsr ( HV_X64_MSR_SIEFP, siefp );
270
+
271
+	/* Enable SynIC */
272
+	scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
273
+	scontrol |= HV_SCONTROL_ENABLE;
274
+	DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
275
+	wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
276
+
277
+	return 0;
278
+}
279
+
280
+/**
281
+ * Unmap synthetic interrupt controller
282
+ *
283
+ * @v hv		Hyper-V hypervisor
284
+ */
285
+static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
286
+	uint64_t scontrol;
287
+	uint64_t siefp;
288
+	uint64_t simp;
289
+
290
+	/* Disable SynIC */
291
+	scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
292
+	scontrol &= ~HV_SCONTROL_ENABLE;
293
+	DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
294
+	wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
295
+
296
+	/* Unmap SynIC event page */
297
+	siefp = rdmsr ( HV_X64_MSR_SIEFP );
298
+	siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
299
+	DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
300
+	wrmsr ( HV_X64_MSR_SIEFP, siefp );
301
+
302
+	/* Unmap SynIC message page */
303
+	simp = rdmsr ( HV_X64_MSR_SIMP );
304
+	simp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIMP_ENABLE );
305
+	DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
306
+	wrmsr ( HV_X64_MSR_SIMP, simp );
307
+}
308
+
309
+/**
310
+ * Enable synthetic interrupt
311
+ *
312
+ * @v hv		Hyper-V hypervisor
313
+ * @v sintx		Synthetic interrupt number
314
+ */
315
+void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
316
+	unsigned long msr = HV_X64_MSR_SINT ( sintx );
317
+	uint64_t sint;
318
+
319
+	/* Enable synthetic interrupt
320
+	 *
321
+	 * We have to enable the interrupt, otherwise messages will
322
+	 * not be delivered (even though the documentation implies
323
+	 * that polling for messages is possible).  We enable AutoEOI
324
+	 * and hook the interrupt to the obsolete IRQ13 (FPU
325
+	 * exception) vector, which will be implemented as a no-op.
326
+	 */
327
+	sint = rdmsr ( msr );
328
+	sint &= ~( HV_SINT_MASKED | HV_SINT_VECTOR_MASK );
329
+	sint |= ( HV_SINT_AUTO_EOI |
330
+		  HV_SINT_VECTOR ( IRQ_INT ( 13 /* See comment above */ ) ) );
331
+	DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
332
+	wrmsr ( msr, sint );
333
+}
334
+
335
+/**
336
+ * Disable synthetic interrupt
337
+ *
338
+ * @v hv		Hyper-V hypervisor
339
+ * @v sintx		Synthetic interrupt number
340
+ */
341
+void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
342
+	unsigned long msr = HV_X64_MSR_SINT ( sintx );
343
+	uint64_t sint;
344
+
345
+	/* Disable synthetic interrupt */
346
+	sint = rdmsr ( msr );
347
+	sint &= ~HV_SINT_AUTO_EOI;
348
+	sint |= HV_SINT_MASKED;
349
+	DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
350
+	wrmsr ( msr, sint );
351
+}
352
+
353
+/**
354
+ * Post message
355
+ *
356
+ * @v hv		Hyper-V hypervisor
357
+ * @v id		Connection ID
358
+ * @v type		Message type
359
+ * @v data		Message
360
+ * @v len		Length of message
361
+ * @ret rc		Return status code
362
+ */
363
+int hv_post_message ( struct hv_hypervisor *hv, unsigned int id,
364
+		      unsigned int type, const void *data, size_t len ) {
365
+	struct hv_post_message *msg = &hv->message->posted;
366
+	int status;
367
+	int rc;
368
+
369
+	/* Sanity check */
370
+	assert ( len <= sizeof ( msg->data ) );
371
+
372
+	/* Construct message */
373
+	memset ( msg, 0, sizeof ( *msg ) );
374
+	msg->id = cpu_to_le32 ( id );
375
+	msg->type = cpu_to_le32 ( type );
376
+	msg->len = cpu_to_le32 ( len );
377
+	memcpy ( msg->data, data, len );
378
+	DBGC2 ( hv, "HV %p connection %d posting message type %#08x:\n",
379
+		hv, id, type );
380
+	DBGC2_HDA ( hv, 0, msg->data, len );
381
+
382
+	/* Post message */
383
+	if ( ( status = hv_call ( hv, HV_POST_MESSAGE, msg, NULL ) ) != 0 ) {
384
+		rc = -EHV ( status );
385
+		DBGC ( hv, "HV %p could not post message to %#08x: %s\n",
386
+		       hv, id, strerror ( rc ) );
387
+		return rc;
388
+	}
389
+
390
+	return 0;
391
+}
392
+
393
+/**
394
+ * Wait for received message
395
+ *
396
+ * @v hv		Hyper-V hypervisor
397
+ * @v sintx		Synthetic interrupt number
398
+ * @ret rc		Return status code
399
+ */
400
+int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ) {
401
+	struct hv_message *msg = &hv->message->received;
402
+	struct hv_message *src = &hv->synic.message[sintx];
403
+	unsigned int retries;
404
+	size_t len;
405
+
406
+	/* Wait for message to arrive */
407
+	for ( retries = 0 ; retries < HV_MESSAGE_MAX_WAIT_MS ; retries++ ) {
408
+
409
+		/* Check for message */
410
+		if ( src->type ) {
411
+
412
+			/* Copy message */
413
+			memset ( msg, 0, sizeof ( *msg ) );
414
+			len = src->len;
415
+			assert ( len <= sizeof ( *msg ) );
416
+			memcpy ( msg, src,
417
+				 ( offsetof ( typeof ( *msg ), data ) + len ) );
418
+			DBGC2 ( hv, "HV %p SINT%d received message type "
419
+				"%#08x:\n", hv, sintx,
420
+				le32_to_cpu ( msg->type ) );
421
+			DBGC2_HDA ( hv, 0, msg->data, len );
422
+
423
+			/* Consume message */
424
+			src->type = 0;
425
+
426
+			return 0;
427
+		}
428
+
429
+		/* Trigger message delivery */
430
+		wrmsr ( HV_X64_MSR_EOM, 0 );
431
+
432
+		/* Delay */
433
+		mdelay ( 1 );
434
+	}
435
+
436
+	DBGC ( hv, "HV %p SINT%d timed out waiting for message\n",
437
+	       hv, sintx );
438
+	return -ETIMEDOUT;
439
+}
440
+
441
+/**
442
+ * Signal event
443
+ *
444
+ * @v hv		Hyper-V hypervisor
445
+ * @v id		Connection ID
446
+ * @v flag		Flag number
447
+ * @ret rc		Return status code
448
+ */
449
+int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id,
450
+		      unsigned int flag ) {
451
+	struct hv_signal_event *event = &hv->message->signalled;
452
+	int status;
453
+	int rc;
454
+
455
+	/* Construct event */
456
+	memset ( event, 0, sizeof ( *event ) );
457
+	event->id = cpu_to_le32 ( id );
458
+	event->flag = cpu_to_le16 ( flag );
459
+
460
+	/* Signal event */
461
+	if ( ( status = hv_call ( hv, HV_SIGNAL_EVENT, event, NULL ) ) != 0 ) {
462
+		rc = -EHV ( status );
463
+		DBGC ( hv, "HV %p could not signal event to %#08x: %s\n",
464
+		       hv, id, strerror ( rc ) );
465
+		return rc;
466
+	}
467
+
468
+	return 0;
469
+}
470
+
471
+/**
472
+ * Probe root device
473
+ *
474
+ * @v rootdev		Root device
475
+ * @ret rc		Return status code
476
+ */
477
+static int hv_probe ( struct root_device *rootdev ) {
478
+	struct hv_hypervisor *hv;
479
+	int rc;
480
+
481
+	/* Allocate and initialise structure */
482
+	hv = zalloc ( sizeof ( *hv ) );
483
+	if ( ! hv ) {
484
+		rc = -ENOMEM;
485
+		goto err_alloc;
486
+	}
487
+
488
+	/* Check we are running in Hyper-V */
489
+	if ( ( rc = hv_check_hv ( hv ) ) != 0 )
490
+		goto err_check_hv;
491
+
492
+	/* Allocate pages */
493
+	if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
494
+				     &hv->synic.event, NULL ) ) != 0 )
495
+		goto err_alloc_pages;
496
+
497
+	/* Allocate message buffer */
498
+	if ( ( rc = hv_alloc_message ( hv ) ) != 0 )
499
+		goto err_alloc_message;
500
+
501
+	/* Map hypercall page */
502
+	if ( ( rc = hv_map_hypercall ( hv ) ) != 0 )
503
+		goto err_map_hypercall;
504
+
505
+	/* Map synthetic interrupt controller */
506
+	if ( ( rc = hv_map_synic ( hv ) ) != 0 )
507
+		goto err_map_synic;
508
+
509
+	rootdev_set_drvdata ( rootdev, hv );
510
+	return 0;
511
+
512
+	hv_unmap_synic ( hv );
513
+ err_map_synic:
514
+	hv_unmap_hypercall ( hv );
515
+ err_map_hypercall:
516
+	hv_free_message ( hv );
517
+ err_alloc_message:
518
+	hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
519
+			NULL );
520
+ err_alloc_pages:
521
+ err_check_hv:
522
+	free ( hv );
523
+ err_alloc:
524
+	return rc;
525
+}
526
+
527
+/**
528
+ * Remove root device
529
+ *
530
+ * @v rootdev		Root device
531
+ */
532
+static void hv_remove ( struct root_device *rootdev ) {
533
+	struct hv_hypervisor *hv = rootdev_get_drvdata ( rootdev );
534
+
535
+	hv_unmap_synic ( hv );
536
+	hv_unmap_hypercall ( hv );
537
+	hv_free_message ( hv );
538
+	hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
539
+			NULL );
540
+	free ( hv );
541
+}
542
+
543
+/** Hyper-V root device driver */
544
+static struct root_driver hv_root_driver = {
545
+	.probe = hv_probe,
546
+	.remove = hv_remove,
547
+};
548
+
549
+/** Hyper-V root device */
550
+struct root_device hv_root_device __root_device = {
551
+	.dev = { .name = "Hyper-V" },
552
+	.driver = &hv_root_driver,
553
+};

+ 42
- 0
src/arch/x86/drivers/hyperv/hyperv.h Целия файл

@@ -0,0 +1,42 @@
1
+#ifndef _HYPERV_H
2
+#define _HYPERV_H
3
+
4
+/** @file
5
+ *
6
+ * Hyper-V driver
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+/** Get vendor identification */
13
+#define HV_CPUID_VENDOR_ID 0x40000000UL
14
+
15
+/** Get interface identification */
16
+#define HV_CPUID_INTERFACE_ID 0x40000001UL
17
+
18
+/** Get hypervisor identification */
19
+#define HV_CPUID_HYPERVISOR_ID 0x40000002UL
20
+
21
+/** Guest OS identity MSR */
22
+#define HV_X64_MSR_GUEST_OS_ID 0x40000000UL
23
+
24
+/** Hypercall page MSR */
25
+#define HV_X64_MSR_HYPERCALL 0x40000001UL
26
+
27
+/** SynIC control MSR */
28
+#define HV_X64_MSR_SCONTROL 0x40000080UL
29
+
30
+/** SynIC event flags page MSR */
31
+#define HV_X64_MSR_SIEFP 0x40000082UL
32
+
33
+/** SynIC message page MSR */
34
+#define HV_X64_MSR_SIMP 0x40000083UL
35
+
36
+/** SynIC end of message MSR */
37
+#define HV_X64_MSR_EOM 0x40000084UL
38
+
39
+/** SynIC interrupt source MSRs */
40
+#define HV_X64_MSR_SINT(x) ( 0x40000090UL + (x) )
41
+
42
+#endif /* _HYPERV_H */

+ 1
- 0
src/arch/x86/include/bits/errfile.h Целия файл

@@ -46,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
46 46
 #define ERRFILE_timer_rdtsc   ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 )
47 47
 #define ERRFILE_timer_bios    ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 )
48 48
 #define ERRFILE_hvm	      ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00020000 )
49
+#define ERRFILE_hyperv	      ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00030000 )
49 50
 
50 51
 #define ERRFILE_cpuid_cmd      ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 )
51 52
 #define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 )

+ 3
- 0
src/arch/x86/include/ipxe/cpuid.h Целия файл

@@ -39,6 +39,9 @@ struct x86_features {
39 39
 /** Get standard features */
40 40
 #define CPUID_FEATURES 0x00000001UL
41 41
 
42
+/** Hypervisor is present */
43
+#define CPUID_FEATURES_INTEL_ECX_HYPERVISOR 0x80000000UL
44
+
42 45
 /** Get largest extended function */
43 46
 #define CPUID_AMD_MAX_FN 0x80000000UL
44 47
 

src/arch/i386/include/pic8259.h → src/arch/x86/include/pic8259.h Целия файл

@@ -11,9 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
11 11
 
12 12
 #include <ipxe/io.h>
13 13
 
14
-/* For segoff_t */
15
-#include "realmode.h"
16
-
17 14
 #define IRQ_PIC_CUTOFF 8
18 15
 
19 16
 /* 8259 register locations */

+ 75
- 0
src/arch/x86_64/include/bits/hyperv.h Целия файл

@@ -0,0 +1,75 @@
1
+#ifndef _BITS_HYPERV_H
2
+#define _BITS_HYPERV_H
3
+
4
+/** @file
5
+ *
6
+ * Hyper-V interface
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stddef.h>
13
+#include <stdint.h>
14
+#include <ipxe/io.h>
15
+
16
+/**
17
+ * Issue hypercall
18
+ *
19
+ * @v hv		Hyper-V hypervisor
20
+ * @v code		Call code
21
+ * @v in		Input parameters
22
+ * @v out		Output parameters
23
+ * @ret status		Status code
24
+ */
25
+static inline __attribute__ (( always_inline )) int
26
+hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in,
27
+	  void *out ) {
28
+	void *hypercall = hv->hypercall;
29
+	register uint64_t rcx asm ( "rcx" );
30
+	register uint64_t rdx asm ( "rdx" );
31
+	register uint64_t r8 asm ( "r8" );
32
+	uint64_t in_phys;
33
+	uint64_t out_phys;
34
+	uint16_t result;
35
+
36
+	in_phys = ( ( __builtin_constant_p ( in ) && ( in == NULL ) )
37
+		       ? 0 : virt_to_phys ( in ) );
38
+	out_phys = ( ( __builtin_constant_p ( out ) && ( out == NULL ) )
39
+		       ? 0 : virt_to_phys ( out ) );
40
+	rcx = code;
41
+	rdx = in_phys;
42
+	r8 = out_phys;
43
+	__asm__ __volatile__ ( "call *%4"
44
+			       : "=a" ( result ), "+r" ( rcx ), "+r" ( rdx ),
45
+				 "+r" ( r8 )
46
+			       : "m" ( hypercall )
47
+			       : "r9", "r10", "r11", "xmm0", "xmm1", "xmm2",
48
+				 "xmm3", "xmm4", "xmm5" );
49
+	return result;
50
+}
51
+
52
+/**
53
+ * Set bit atomically
54
+ *
55
+ * @v bits		Bit field
56
+ * @v bit		Bit to set
57
+ */
58
+static inline __attribute__ (( always_inline )) void
59
+hv_set_bit ( void *bits, unsigned int bit ) {
60
+	struct {
61
+		uint64_t qword[ ( bit / 64 ) + 1 ];
62
+	} *qwords = bits;
63
+
64
+	/* Set bit using "lock bts".  Inform compiler that any memory
65
+	 * from the start of the bit field up to and including the
66
+	 * qword containing this bit may be modified.  (This is
67
+	 * overkill but shouldn't matter in practice since we're
68
+	 * unlikely to subsequently read other bits from the same bit
69
+	 * field.)
70
+	 */
71
+	__asm__ __volatile__ ( "lock bts %1, %0"
72
+			       : "+m" ( *qwords ) : "Ir" ( bit ) );
73
+}
74
+
75
+#endif /* _BITS_HYPERV_H */

+ 230
- 0
src/include/ipxe/hyperv.h Целия файл

@@ -0,0 +1,230 @@
1
+#ifndef _IPXE_HYPERV_H
2
+#define _IPXE_HYPERV_H
3
+
4
+/** @file
5
+ *
6
+ * Hyper-V interface
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/io.h>
14
+
15
+/** Hyper-V interface identification */
16
+#define HV_INTERFACE_ID 0x31237648 /* "Hv#1" */
17
+
18
+/** Guest OS identity for iPXE
19
+ *
20
+ * This field comprises:
21
+ *
22
+ * Bit  63    : set to 1 to indicate an open source OS
23
+ * Bits 62:56 : OS Type
24
+ * Bits 55:48 : OS ID
25
+ * Bits 47:16 : Version
26
+ * Bits 15:0  : Build number
27
+ *
28
+ * There appears to be no central registry for the "OS Type".  The
29
+ * specification states that "Linux is 0x100", and the FreeBSD source
30
+ * states that "FreeBSD is 0x200".  Both of these statements are
31
+ * actually referring to the combined "OS Type" and "OS ID" field.
32
+ *
33
+ * We choose to use 0x98ae: this is generated by setting bit 63 (to
34
+ * indicate an open source OS) and setting the OS Type+ID equal to the
35
+ * PnP vendor ID used in romprefix.S.  No version information or build
36
+ * number is included.
37
+ */
38
+#define HV_GUEST_OS_ID_IPXE ( ( 1ULL << 63 ) | ( 0x18aeULL << 48 ) )
39
+
40
+/** Enable hypercall page */
41
+#define HV_HYPERCALL_ENABLE 0x00000001UL
42
+
43
+/** Enable SynIC */
44
+#define HV_SCONTROL_ENABLE 0x00000001UL
45
+
46
+/** Enable SynIC event flags */
47
+#define HV_SIEFP_ENABLE 0x00000001UL
48
+
49
+/** Enable SynIC messages */
50
+#define HV_SIMP_ENABLE 0x00000001UL
51
+
52
+/** Perform implicit EOI upon synthetic interrupt delivery */
53
+#define HV_SINT_AUTO_EOI 0x00020000UL
54
+
55
+/** Mask synthetic interrupt */
56
+#define HV_SINT_MASKED 0x00010000UL
57
+
58
+/** Synthetic interrupt vector */
59
+#define HV_SINT_VECTOR(x) ( (x) << 0 )
60
+
61
+/** Synthetic interrupt vector mask */
62
+#define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff )
63
+
64
+/** Post message */
65
+#define HV_POST_MESSAGE 0x005c
66
+
67
+/** A posted message
68
+ *
69
+ * This is the input parameter list for the HvPostMessage hypercall.
70
+ */
71
+struct hv_post_message {
72
+	/** Connection ID */
73
+	uint32_t id;
74
+	/** Padding */
75
+	uint32_t reserved;
76
+	/** Type */
77
+	uint32_t type;
78
+	/** Length of message */
79
+	uint32_t len;
80
+	/** Message */
81
+	uint8_t data[240];
82
+} __attribute__ (( packed ));
83
+
84
+/** A received message
85
+ *
86
+ * This is the HV_MESSAGE structure from the Hypervisor Top-Level
87
+ * Functional Specification.  The field order given in the
88
+ * documentation is incorrect.
89
+ */
90
+struct hv_message {
91
+	/** Type */
92
+	uint32_t type;
93
+	/** Length of message */
94
+	uint8_t len;
95
+	/** Flags */
96
+	uint8_t flags;
97
+	/** Padding */
98
+	uint16_t reserved;
99
+	/** Origin */
100
+	uint64_t origin;
101
+	/** Message */
102
+	uint8_t data[240];
103
+} __attribute__ (( packed ));
104
+
105
+/** Signal event */
106
+#define HV_SIGNAL_EVENT 0x005d
107
+
108
+/** A signalled event */
109
+struct hv_signal_event {
110
+	/** Connection ID */
111
+	uint32_t id;
112
+	/** Flag number */
113
+	uint16_t flag;
114
+	/** Reserved */
115
+	uint16_t reserved;
116
+} __attribute__ (( packed ));
117
+
118
+/** A received event */
119
+struct hv_event {
120
+	/** Event flags */
121
+	uint8_t flags[256];
122
+} __attribute__ (( packed ));
123
+
124
+/** A monitor trigger group
125
+ *
126
+ * This is the HV_MONITOR_TRIGGER_GROUP structure from the Hypervisor
127
+ * Top-Level Functional Specification.
128
+ */
129
+struct hv_monitor_trigger {
130
+	/** Pending events */
131
+	uint32_t pending;
132
+	/** Armed events */
133
+	uint32_t armed;
134
+} __attribute__ (( packed ));
135
+
136
+/** A monitor parameter set
137
+ *
138
+ * This is the HV_MONITOR_PARAMETER structure from the Hypervisor
139
+ * Top-Level Functional Specification.
140
+ */
141
+struct hv_monitor_parameter {
142
+	/** Connection ID */
143
+	uint32_t id;
144
+	/** Flag number */
145
+	uint16_t flag;
146
+	/** Reserved */
147
+	uint16_t reserved;
148
+} __attribute__ (( packed ));
149
+
150
+/** A monitor page
151
+ *
152
+ * This is the HV_MONITOR_PAGE structure from the Hypervisor Top-Level
153
+ * Functional Specification.
154
+ */
155
+struct hv_monitor {
156
+	/** Flags */
157
+	uint32_t flags;
158
+	/** Reserved */
159
+	uint8_t reserved_a[4];
160
+	/** Trigger groups */
161
+	struct hv_monitor_trigger trigger[4];
162
+	/** Reserved */
163
+	uint8_t reserved_b[536];
164
+	/** Latencies */
165
+	uint16 latency[4][32];
166
+	/** Reserved */
167
+	uint8_t reserved_c[256];
168
+	/** Parameters */
169
+	struct hv_monitor_parameter param[4][32];
170
+	/** Reserved */
171
+	uint8_t reserved_d[1984];
172
+} __attribute__ (( packed ));
173
+
174
+/** A synthetic interrupt controller */
175
+struct hv_synic {
176
+	/** Message page */
177
+	struct hv_message *message;
178
+	/** Event flag page */
179
+	struct hv_event *event;
180
+};
181
+
182
+/** A message buffer */
183
+union hv_message_buffer {
184
+	/** Posted message */
185
+	struct hv_post_message posted;
186
+	/** Received message */
187
+	struct hv_message received;
188
+	/** Signalled event */
189
+	struct hv_signal_event signalled;
190
+};
191
+
192
+/** A Hyper-V hypervisor */
193
+struct hv_hypervisor {
194
+	/** Hypercall page */
195
+	void *hypercall;
196
+	/** Synthetic interrupt controller (SynIC) */
197
+	struct hv_synic synic;
198
+	/** Message buffer */
199
+	union hv_message_buffer *message;
200
+};
201
+
202
+#include <bits/hyperv.h>
203
+
204
+/**
205
+ * Calculate the number of pages covering an address range
206
+ *
207
+ * @v data		Start of data
208
+ * @v len		Length of data (must be non-zero)
209
+ * @ret pfn_count	Number of pages covered
210
+ */
211
+static inline unsigned int hv_pfn_count ( physaddr_t data, size_t len ) {
212
+	unsigned int first_pfn = ( data / PAGE_SIZE );
213
+	unsigned int last_pfn = ( ( data + len - 1 ) / PAGE_SIZE );
214
+
215
+	return ( last_pfn - first_pfn + 1 );
216
+}
217
+
218
+extern __attribute__ (( sentinel )) int
219
+hv_alloc_pages ( struct hv_hypervisor *hv, ... );
220
+extern __attribute__ (( sentinel )) void
221
+hv_free_pages ( struct hv_hypervisor *hv, ... );
222
+extern void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx );
223
+extern void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx );
224
+extern int hv_post_message ( struct hv_hypervisor *hv, unsigned int id,
225
+			     unsigned int type, const void *data, size_t len );
226
+extern int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx );
227
+extern int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id,
228
+			     unsigned int flag );
229
+
230
+#endif /* _IPXE_HYPERV_H */

Loading…
Отказ
Запис