Browse Source

[xen] Add basic support for PV-HVM domains

Add basic support for Xen PV-HVM domains (detected via the Xen
platform PCI device with IDs 5853:0001), including support for
accessing configuration via XenStore and enumerating devices via
XenBus.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
036af27a45

+ 1
- 0
src/Makefile View File

@@ -84,6 +84,7 @@ SRCDIRS		+= drivers/bitbash
84 84
 SRCDIRS		+= drivers/infiniband
85 85
 SRCDIRS		+= interface/pxe interface/efi interface/smbios
86 86
 SRCDIRS		+= interface/bofm
87
+SRCDIRS		+= interface/xen
87 88
 SRCDIRS		+= tests
88 89
 SRCDIRS		+= crypto crypto/axtls crypto/matrixssl
89 90
 SRCDIRS		+= hci hci/commands hci/tui

+ 1
- 0
src/arch/x86/Makefile View File

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

+ 521
- 0
src/arch/x86/drivers/xen/hvm.c View File

@@ -0,0 +1,521 @@
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
+#include <stdint.h>
23
+#include <stdio.h>
24
+#include <errno.h>
25
+#include <ipxe/malloc.h>
26
+#include <ipxe/pci.h>
27
+#include <ipxe/cpuid.h>
28
+#include <ipxe/msr.h>
29
+#include <ipxe/xen.h>
30
+#include <ipxe/xenver.h>
31
+#include <ipxe/xenmem.h>
32
+#include <ipxe/xenstore.h>
33
+#include <ipxe/xenbus.h>
34
+#include <ipxe/xengrant.h>
35
+#include "hvm.h"
36
+
37
+/** @file
38
+ *
39
+ * Xen HVM driver
40
+ *
41
+ */
42
+
43
+/**
44
+ * Get CPUID base
45
+ *
46
+ * @v hvm		HVM device
47
+ * @ret rc		Return status code
48
+ */
49
+static int hvm_cpuid_base ( struct hvm_device *hvm ) {
50
+	struct {
51
+		uint32_t ebx;
52
+		uint32_t ecx;
53
+		uint32_t edx;
54
+	} __attribute__ (( packed )) signature;
55
+	uint32_t base;
56
+	uint32_t version;
57
+	uint32_t discard_eax;
58
+	uint32_t discard_ebx;
59
+	uint32_t discard_ecx;
60
+	uint32_t discard_edx;
61
+
62
+	/* Scan for magic signature */
63
+	for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
64
+	      base += HVM_CPUID_STEP ) {
65
+		cpuid ( base, &discard_eax, &signature.ebx, &signature.ecx,
66
+			&signature.edx );
67
+		if ( memcmp ( &signature, HVM_CPUID_MAGIC,
68
+			      sizeof ( signature ) ) == 0 ) {
69
+			hvm->cpuid_base = base;
70
+			cpuid ( ( base + HVM_CPUID_VERSION ), &version,
71
+				&discard_ebx, &discard_ecx, &discard_edx );
72
+			DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
73
+				base, ( version >> 16 ), ( version & 0xffff ) );
74
+			return 0;
75
+		}
76
+	}
77
+
78
+	DBGC ( hvm, "HVM could not find hypervisor\n" );
79
+	return -ENODEV;
80
+}
81
+
82
+/**
83
+ * Map hypercall page(s)
84
+ *
85
+ * @v hvm		HVM device
86
+ * @ret rc		Return status code
87
+ */
88
+static int hvm_map_hypercall ( struct hvm_device *hvm ) {
89
+	uint32_t pages;
90
+	uint32_t msr;
91
+	uint32_t discard_ecx;
92
+	uint32_t discard_edx;
93
+	physaddr_t hypercall_phys;
94
+	uint32_t version;
95
+	static xen_extraversion_t extraversion;
96
+	int xenrc;
97
+	int rc;
98
+
99
+	/* Get number of hypercall pages and MSR to use */
100
+	cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), &pages, &msr,
101
+		&discard_ecx, &discard_edx );
102
+
103
+	/* Allocate pages */
104
+	hvm->hypercall_len = ( pages * PAGE_SIZE );
105
+	hvm->xen.hypercall = malloc_dma ( hvm->hypercall_len, PAGE_SIZE );
106
+	if ( ! hvm->xen.hypercall ) {
107
+		DBGC ( hvm, "HVM could not allocate %d hypercall page(s)\n",
108
+		       pages );
109
+		return -ENOMEM;
110
+	}
111
+	hypercall_phys = virt_to_phys ( hvm->xen.hypercall );
112
+	DBGC2 ( hvm, "HVM hypercall page(s) at [%#08lx,%#08lx) via MSR %#08x\n",
113
+		hypercall_phys, ( hypercall_phys + hvm->hypercall_len ), msr );
114
+
115
+	/* Write to MSR */
116
+	wrmsr ( msr, hypercall_phys );
117
+
118
+	/* Check that hypercall mechanism is working */
119
+	version = xenver_version ( &hvm->xen );
120
+	if ( ( xenrc = xenver_extraversion ( &hvm->xen, &extraversion ) ) != 0){
121
+		rc = -EXEN ( xenrc );
122
+		DBGC ( hvm, "HVM could not get extraversion: %s\n",
123
+		       strerror ( rc ) );
124
+		return rc;
125
+	}
126
+	DBGC2 ( hvm, "HVM found Xen version %d.%d%s\n",
127
+		( version >> 16 ), ( version & 0xffff ) , extraversion );
128
+
129
+	return 0;
130
+}
131
+
132
+/**
133
+ * Unmap hypercall page(s)
134
+ *
135
+ * @v hvm		HVM device
136
+ */
137
+static void hvm_unmap_hypercall ( struct hvm_device *hvm ) {
138
+
139
+	/* Free pages */
140
+	free_dma ( hvm->xen.hypercall, hvm->hypercall_len );
141
+}
142
+
143
+/**
144
+ * Allocate and map MMIO space
145
+ *
146
+ * @v hvm		HVM device
147
+ * @v space		Source mapping space
148
+ * @v pages		Number of pages
149
+ * @ret mmio		MMIO space address, or NULL on error
150
+ */
151
+static void * hvm_ioremap ( struct hvm_device *hvm, unsigned int space,
152
+			    unsigned int pages ) {
153
+	struct xen_add_to_physmap add;
154
+	struct xen_remove_from_physmap remove;
155
+	physaddr_t mmio_phys;
156
+	unsigned int i;
157
+	size_t len;
158
+	void *mmio;
159
+	int xenrc;
160
+	int rc;
161
+
162
+	/* Check for available space */
163
+	len = ( pages * PAGE_SIZE );
164
+	if ( ( hvm->mmio_offset + len ) > hvm->mmio_len ) {
165
+		DBGC ( hvm, "HVM could not allocate %zd bytes of MMIO space "
166
+		       "(%zd of %zd remaining)\n", len,
167
+		       ( hvm->mmio_len - hvm->mmio_offset ), hvm->mmio_len );
168
+		goto err_no_space;
169
+	}
170
+
171
+	/* Map this space */
172
+	mmio = ioremap ( ( hvm->mmio + hvm->mmio_offset ), len );
173
+	if ( ! mmio ) {
174
+		DBGC ( hvm, "HVM could not map MMIO space [%08lx,%08lx)\n",
175
+		       ( hvm->mmio + hvm->mmio_offset ),
176
+		       ( hvm->mmio + hvm->mmio_offset + len ) );
177
+		goto err_ioremap;
178
+	}
179
+	mmio_phys = virt_to_phys ( mmio );
180
+
181
+	/* Add to physical address space */
182
+	for ( i = 0 ; i < pages ; i++ ) {
183
+		add.domid = DOMID_SELF;
184
+		add.idx = i;
185
+		add.space = space;
186
+		add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
187
+		if ( ( xenrc = xenmem_add_to_physmap ( &hvm->xen, &add ) ) !=0){
188
+			rc = -EXEN ( xenrc );
189
+			DBGC ( hvm, "HVM could not add space %d idx %d at "
190
+			       "[%08lx,%08lx): %s\n", space, i,
191
+			       ( mmio_phys + ( i * PAGE_SIZE ) ),
192
+			       ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
193
+			       strerror ( rc ) );
194
+			goto err_add_to_physmap;
195
+		}
196
+	}
197
+
198
+	/* Update offset */
199
+	hvm->mmio_offset += len;
200
+
201
+	return mmio;
202
+
203
+	i = pages;
204
+ err_add_to_physmap:
205
+	for ( i-- ; ( signed int ) i >= 0 ; i-- ) {
206
+		remove.domid = DOMID_SELF;
207
+		add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
208
+		xenmem_remove_from_physmap ( &hvm->xen, &remove );
209
+	}
210
+	iounmap ( mmio );
211
+ err_ioremap:
212
+ err_no_space:
213
+	return NULL;
214
+}
215
+
216
+/**
217
+ * Unmap MMIO space
218
+ *
219
+ * @v hvm		HVM device
220
+ * @v mmio		MMIO space address
221
+ * @v pages		Number of pages
222
+ */
223
+static void hvm_iounmap ( struct hvm_device *hvm, void *mmio,
224
+			  unsigned int pages ) {
225
+	struct xen_remove_from_physmap remove;
226
+	physaddr_t mmio_phys = virt_to_phys ( mmio );
227
+	unsigned int i;
228
+	int xenrc;
229
+	int rc;
230
+
231
+	/* Unmap this space */
232
+	iounmap ( mmio );
233
+
234
+	/* Remove from physical address space */
235
+	for ( i = 0 ; i < pages ; i++ ) {
236
+		remove.domid = DOMID_SELF;
237
+		remove.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
238
+		if ( ( xenrc = xenmem_remove_from_physmap ( &hvm->xen,
239
+							    &remove ) ) != 0 ) {
240
+			rc = -EXEN ( xenrc );
241
+			DBGC ( hvm, "HVM could not remove space [%08lx,%08lx): "
242
+			       "%s\n", ( mmio_phys + ( i * PAGE_SIZE ) ),
243
+			       ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
244
+			       strerror ( rc ) );
245
+			/* Nothing we can do about this */
246
+		}
247
+	}
248
+}
249
+
250
+/**
251
+ * Map shared info page
252
+ *
253
+ * @v hvm		HVM device
254
+ * @ret rc		Return status code
255
+ */
256
+static int hvm_map_shared_info ( struct hvm_device *hvm ) {
257
+	physaddr_t shared_info_phys;
258
+	int rc;
259
+
260
+	/* Map shared info page */
261
+	hvm->xen.shared = hvm_ioremap ( hvm, XENMAPSPACE_shared_info, 1 );
262
+	if ( ! hvm->xen.shared ) {
263
+		rc = -ENOMEM;
264
+		goto err_alloc;
265
+	}
266
+	shared_info_phys = virt_to_phys ( hvm->xen.shared );
267
+	DBGC2 ( hvm, "HVM shared info page at [%#08lx,%#08lx)\n",
268
+		shared_info_phys, ( shared_info_phys + PAGE_SIZE ) );
269
+
270
+	/* Sanity check */
271
+	DBGC2 ( hvm, "HVM wallclock time is %d\n",
272
+		readl ( &hvm->xen.shared->wc_sec ) );
273
+
274
+	return 0;
275
+
276
+	hvm_iounmap ( hvm, hvm->xen.shared, 1 );
277
+ err_alloc:
278
+	return rc;
279
+}
280
+
281
+/**
282
+ * Unmap shared info page
283
+ *
284
+ * @v hvm		HVM device
285
+ */
286
+static void hvm_unmap_shared_info ( struct hvm_device *hvm ) {
287
+
288
+	/* Unmap shared info page */
289
+	hvm_iounmap ( hvm, hvm->xen.shared, 1 );
290
+}
291
+
292
+/**
293
+ * Map grant table
294
+ *
295
+ * @v hvm		HVM device
296
+ * @ret rc		Return status code
297
+ */
298
+static int hvm_map_grant ( struct hvm_device *hvm ) {
299
+	struct gnttab_query_size size;
300
+	struct gnttab_set_version version;
301
+	physaddr_t grant_phys;
302
+	size_t len;
303
+	int xenrc;
304
+	int rc;
305
+
306
+	/* Get grant table size */
307
+	size.dom = DOMID_SELF;
308
+	if ( ( xenrc = xengrant_query_size ( &hvm->xen, &size ) ) != 0 ) {
309
+		rc = -EXEN ( xenrc );
310
+		DBGC ( hvm, "HVM could not get grant table size: %s\n",
311
+		       strerror ( rc ) );
312
+		goto err_query_size;
313
+	}
314
+	len = ( size.nr_frames * PAGE_SIZE );
315
+
316
+	/* Configure to use version 2 tables */
317
+	version.version = 2;
318
+	if ( ( xenrc = xengrant_set_version ( &hvm->xen, &version ) ) != 0 ) {
319
+		rc = -EXEN ( xenrc );
320
+		DBGC ( hvm, "HVM could not set version 2 grant table: %s\n",
321
+		       strerror ( rc ) );
322
+		goto err_set_version;
323
+	}
324
+	if ( version.version != 2 ) {
325
+		DBGC ( hvm, "HVM could not set version 2 grant table\n" );
326
+		rc = -ENOTTY;
327
+		goto err_set_version;
328
+	}
329
+
330
+	/* Map grant table */
331
+	hvm->xen.grant.table = hvm_ioremap ( hvm, XENMAPSPACE_grant_table,
332
+					     size.nr_frames );
333
+	if ( ! hvm->xen.grant.table ) {
334
+		rc = -ENODEV;
335
+		goto err_ioremap;
336
+	}
337
+	grant_phys = virt_to_phys ( hvm->xen.grant.table );
338
+	DBGC2 ( hvm, "HVM mapped grant table at [%08lx,%08lx)\n",
339
+		grant_phys, ( grant_phys + len ) );
340
+	hvm->xen.grant.count = ( len / sizeof ( hvm->xen.grant.table[0] ) );
341
+
342
+	return 0;
343
+
344
+	hvm_iounmap ( hvm, hvm->xen.grant.table, size.nr_frames );
345
+ err_ioremap:
346
+ err_set_version:
347
+ err_query_size:
348
+	return rc;
349
+}
350
+
351
+/**
352
+ * Unmap grant table
353
+ *
354
+ * @v hvm		HVM device
355
+ */
356
+static void hvm_unmap_grant ( struct hvm_device *hvm ) {
357
+	size_t len;
358
+
359
+	/* Unmap grant table */
360
+	len = ( hvm->xen.grant.count * sizeof ( hvm->xen.grant.table[0] ) );
361
+	hvm_iounmap ( hvm, hvm->xen.grant.table, ( len / PAGE_SIZE ) );
362
+}
363
+
364
+/**
365
+ * Map XenStore
366
+ *
367
+ * @v hvm		HVM device
368
+ * @ret rc		Return status code
369
+ */
370
+static int hvm_map_xenstore ( struct hvm_device *hvm ) {
371
+	uint64_t xenstore_evtchn;
372
+	uint64_t xenstore_pfn;
373
+	physaddr_t xenstore_phys;
374
+	char *name;
375
+	int xenrc;
376
+	int rc;
377
+
378
+	/* Get XenStore event channel */
379
+	if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_EVTCHN,
380
+					   &xenstore_evtchn ) ) != 0 ) {
381
+		rc = -EXEN ( xenrc );
382
+		DBGC ( hvm, "HVM could not get XenStore event channel: %s\n",
383
+		       strerror ( rc ) );
384
+		return rc;
385
+	}
386
+	hvm->xen.store.port = xenstore_evtchn;
387
+
388
+	/* Get XenStore PFN */
389
+	if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_PFN,
390
+					   &xenstore_pfn ) ) != 0 ) {
391
+		rc = -EXEN ( xenrc );
392
+		DBGC ( hvm, "HVM could not get XenStore PFN: %s\n",
393
+		       strerror ( rc ) );
394
+		return rc;
395
+	}
396
+	xenstore_phys = ( xenstore_pfn * PAGE_SIZE );
397
+
398
+	/* Map XenStore */
399
+	hvm->xen.store.intf = ioremap ( xenstore_phys, PAGE_SIZE );
400
+	if ( ! hvm->xen.store.intf ) {
401
+		DBGC ( hvm, "HVM could not map XenStore at [%08lx,%08lx)\n",
402
+		       xenstore_phys, ( xenstore_phys + PAGE_SIZE ) );
403
+		return -ENODEV;
404
+	}
405
+	DBGC2 ( hvm, "HVM mapped XenStore at [%08lx,%08lx) with event port "
406
+		"%d\n", xenstore_phys, ( xenstore_phys + PAGE_SIZE ),
407
+		hvm->xen.store.port );
408
+
409
+	/* Check that XenStore is working */
410
+	if ( ( rc = xenstore_read ( &hvm->xen, &name, "name", NULL ) ) != 0 ) {
411
+		DBGC ( hvm, "HVM could not read domain name: %s\n",
412
+		       strerror ( rc ) );
413
+		return rc;
414
+	}
415
+	DBGC2 ( hvm, "HVM running in domain \"%s\"\n", name );
416
+	free ( name );
417
+
418
+	return 0;
419
+}
420
+
421
+/**
422
+ * Unmap XenStore
423
+ *
424
+ * @v hvm		HVM device
425
+ */
426
+static void hvm_unmap_xenstore ( struct hvm_device *hvm ) {
427
+
428
+	/* Unmap XenStore */
429
+	iounmap ( hvm->xen.store.intf );
430
+}
431
+
432
+/**
433
+ * Probe PCI device
434
+ *
435
+ * @v pci		PCI device
436
+ * @ret rc		Return status code
437
+ */
438
+static int hvm_probe ( struct pci_device *pci ) {
439
+	struct hvm_device *hvm;
440
+	int rc;
441
+
442
+	/* Allocate and initialise structure */
443
+	hvm = zalloc ( sizeof ( *hvm ) );
444
+	if ( ! hvm ) {
445
+		rc = -ENOMEM;
446
+		goto err_alloc;
447
+	}
448
+	hvm->mmio = pci_bar_start ( pci, HVM_MMIO_BAR );
449
+	hvm->mmio_len = pci_bar_size ( pci, HVM_MMIO_BAR );
450
+	DBGC2 ( hvm, "HVM has MMIO space [%08lx,%08lx)\n",
451
+		hvm->mmio, ( hvm->mmio + hvm->mmio_len ) );
452
+
453
+	/* Fix up PCI device */
454
+	adjust_pci_device ( pci );
455
+
456
+	/* Attach to hypervisor */
457
+	if ( ( rc = hvm_cpuid_base ( hvm ) ) != 0 )
458
+		goto err_cpuid_base;
459
+	if ( ( rc = hvm_map_hypercall ( hvm ) ) != 0 )
460
+		goto err_map_hypercall;
461
+	if ( ( rc = hvm_map_shared_info ( hvm ) ) != 0 )
462
+		goto err_map_shared_info;
463
+	if ( ( rc = hvm_map_grant ( hvm ) ) != 0 )
464
+		goto err_map_grant;
465
+	if ( ( rc = hvm_map_xenstore ( hvm ) ) != 0 )
466
+		goto err_map_xenstore;
467
+
468
+	/* Probe Xen devices */
469
+	if ( ( rc = xenbus_probe ( &hvm->xen, &pci->dev ) ) != 0 ) {
470
+		DBGC ( hvm, "HVM could not probe Xen bus: %s\n",
471
+		       strerror ( rc ) );
472
+		goto err_xenbus_probe;
473
+	}
474
+
475
+	pci_set_drvdata ( pci, hvm );
476
+	return 0;
477
+
478
+	xenbus_remove ( &hvm->xen, &pci->dev );
479
+ err_xenbus_probe:
480
+	hvm_unmap_xenstore ( hvm );
481
+ err_map_xenstore:
482
+	hvm_unmap_grant ( hvm );
483
+ err_map_grant:
484
+	hvm_unmap_shared_info ( hvm );
485
+ err_map_shared_info:
486
+	hvm_unmap_hypercall ( hvm );
487
+ err_map_hypercall:
488
+ err_cpuid_base:
489
+	free ( hvm );
490
+ err_alloc:
491
+	return rc;
492
+}
493
+
494
+/**
495
+ * Remove PCI device
496
+ *
497
+ * @v pci		PCI device
498
+ */
499
+static void hvm_remove ( struct pci_device *pci ) {
500
+	struct hvm_device *hvm = pci_get_drvdata ( pci );
501
+
502
+	xenbus_remove ( &hvm->xen, &pci->dev );
503
+	hvm_unmap_xenstore ( hvm );
504
+	hvm_unmap_grant ( hvm );
505
+	hvm_unmap_shared_info ( hvm );
506
+	hvm_unmap_hypercall ( hvm );
507
+	free ( hvm );
508
+}
509
+
510
+/** PCI device IDs */
511
+static struct pci_device_id hvm_ids[] = {
512
+	PCI_ROM ( 0x5853, 0x0001, "hvm", "hvm", 0 ),
513
+};
514
+
515
+/** PCI driver */
516
+struct pci_driver hvm_driver __pci_driver = {
517
+	.ids = hvm_ids,
518
+	.id_count = ( sizeof ( hvm_ids ) / sizeof ( hvm_ids[0] ) ),
519
+	.probe = hvm_probe,
520
+	.remove = hvm_remove,
521
+};

+ 75
- 0
src/arch/x86/drivers/xen/hvm.h View File

@@ -0,0 +1,75 @@
1
+#ifndef _HVM_H
2
+#define _HVM_H
3
+
4
+/** @file
5
+ *
6
+ * Xen HVM driver
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/xen.h>
14
+#include <xen/hvm/hvm_op.h>
15
+#include <xen/hvm/params.h>
16
+
17
+/** Minimum CPUID base */
18
+#define HVM_CPUID_MIN 0x40000000UL
19
+
20
+/** Maximum CPUID base */
21
+#define HVM_CPUID_MAX 0x4000ff00UL
22
+
23
+/** Increment between CPUID bases */
24
+#define HVM_CPUID_STEP 0x00000100UL
25
+
26
+/** Magic signature */
27
+#define HVM_CPUID_MAGIC "XenVMMXenVMM"
28
+
29
+/** Get Xen version */
30
+#define HVM_CPUID_VERSION 1
31
+
32
+/** Get number of hypercall pages */
33
+#define HVM_CPUID_PAGES 2
34
+
35
+/** PCI MMIO BAR */
36
+#define HVM_MMIO_BAR PCI_BASE_ADDRESS_1
37
+
38
+/** A Xen HVM device */
39
+struct hvm_device {
40
+	/** Xen hypervisor */
41
+	struct xen_hypervisor xen;
42
+	/** CPUID base */
43
+	uint32_t cpuid_base;
44
+	/** Length of hypercall table */
45
+	size_t hypercall_len;
46
+	/** MMIO base address */
47
+	unsigned long mmio;
48
+	/** Current offset within MMIO address space */
49
+	size_t mmio_offset;
50
+	/** Length of MMIO address space */
51
+	size_t mmio_len;
52
+};
53
+
54
+/**
55
+ * Get HVM parameter value
56
+ *
57
+ * @v xen		Xen hypervisor
58
+ * @v index		Parameter index
59
+ * @v value		Value to fill in
60
+ * @ret xenrc		Xen status code
61
+ */
62
+static inline int xen_hvm_get_param ( struct xen_hypervisor *xen,
63
+				      unsigned int index, uint64_t *value ) {
64
+	struct xen_hvm_param param;
65
+	int xenrc;
66
+
67
+	param.domid = DOMID_SELF;
68
+	param.index = index;
69
+	xenrc = xen_hypercall_2 ( xen, __HYPERVISOR_hvm_op, HVMOP_get_param,
70
+				  virt_to_phys ( &param ) );
71
+	*value = param.value;
72
+	return xenrc;
73
+}
74
+
75
+#endif /* _HVM_H */

+ 1
- 0
src/arch/x86/include/bits/errfile.h View File

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

+ 164
- 0
src/arch/x86/include/bits/xen.h View File

@@ -0,0 +1,164 @@
1
+#ifndef _BITS_XEN_H
2
+#define _BITS_XEN_H
3
+
4
+/** @file
5
+ *
6
+ * Xen interface
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+/* Hypercall registers */
13
+#ifdef __x86_64__
14
+#define XEN_REG1 "rdi"
15
+#define XEN_REG2 "rsi"
16
+#define XEN_REG3 "rdx"
17
+#define XEN_REG4 "r10"
18
+#define XEN_REG5 "r8"
19
+#else
20
+#define XEN_REG1 "ebx"
21
+#define XEN_REG2 "ecx"
22
+#define XEN_REG3 "edx"
23
+#define XEN_REG4 "esi"
24
+#define XEN_REG5 "edi"
25
+#endif
26
+
27
+/** A hypercall entry point */
28
+struct xen_hypercall {
29
+	/** Code generated by hypervisor */
30
+	uint8_t code[32];
31
+} __attribute__ (( packed ));
32
+
33
+/**
34
+ * Issue hypercall with one argument
35
+ *
36
+ * @v xen		Xen hypervisor
37
+ * @v hypercall		Hypercall number
38
+ * @v arg1		First argument
39
+ * @ret retval		Return value
40
+ */
41
+static inline __attribute__ (( always_inline )) unsigned long
42
+xen_hypercall_1 ( struct xen_hypervisor *xen, unsigned int hypercall,
43
+		  unsigned long arg1 ) {
44
+	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
45
+	unsigned long retval;
46
+
47
+	__asm__ __volatile__ ( "call *%2"
48
+			       : "=a" ( retval ), "+r" ( reg1 )
49
+			       : "r" ( &xen->hypercall[hypercall] )
50
+			       : XEN_REG2, XEN_REG3, XEN_REG4, XEN_REG5,
51
+				 "memory" );
52
+	return retval;
53
+}
54
+
55
+/**
56
+ * Issue hypercall with two arguments
57
+ *
58
+ * @v xen		Xen hypervisor
59
+ * @v hypercall		Hypercall number
60
+ * @v arg1		First argument
61
+ * @v arg2		Second argument
62
+ * @ret retval		Return value
63
+ */
64
+static inline __attribute__ (( always_inline )) unsigned long
65
+xen_hypercall_2 ( struct xen_hypervisor *xen, unsigned int hypercall,
66
+		  unsigned long arg1, unsigned long arg2 ) {
67
+	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
68
+	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
69
+	unsigned long retval;
70
+
71
+	__asm__ __volatile__ ( "call *%3"
72
+			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 )
73
+			       : "r" ( &xen->hypercall[hypercall] )
74
+			       : XEN_REG3, XEN_REG4, XEN_REG5, "memory" );
75
+	return retval;
76
+}
77
+
78
+/**
79
+ * Issue hypercall with three arguments
80
+ *
81
+ * @v xen		Xen hypervisor
82
+ * @v hypercall		Hypercall number
83
+ * @v arg1		First argument
84
+ * @v arg2		Second argument
85
+ * @v arg3		Third argument
86
+ * @ret retval		Return value
87
+ */
88
+static inline __attribute__ (( always_inline )) unsigned long
89
+xen_hypercall_3 ( struct xen_hypervisor *xen, unsigned int hypercall,
90
+		  unsigned long arg1, unsigned long arg2, unsigned long arg3 ) {
91
+	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
92
+	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
93
+	register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
94
+	unsigned long retval;
95
+
96
+	__asm__ __volatile__ ( "call *%4"
97
+			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
98
+				 "+r" ( reg3 )
99
+			       : "r" ( &xen->hypercall[hypercall] )
100
+			       : XEN_REG4, XEN_REG5, "memory" );
101
+	return retval;
102
+}
103
+
104
+/**
105
+ * Issue hypercall with four arguments
106
+ *
107
+ * @v xen		Xen hypervisor
108
+ * @v hypercall		Hypercall number
109
+ * @v arg1		First argument
110
+ * @v arg2		Second argument
111
+ * @v arg3		Third argument
112
+ * @v arg4		Fourth argument
113
+ * @ret retval		Return value
114
+ */
115
+static inline __attribute__ (( always_inline )) unsigned long
116
+xen_hypercall_4 ( struct xen_hypervisor *xen, unsigned int hypercall,
117
+		  unsigned long arg1, unsigned long arg2, unsigned long arg3,
118
+		  unsigned long arg4 ) {
119
+	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
120
+	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
121
+	register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
122
+	register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
123
+	unsigned long retval;
124
+
125
+	__asm__ __volatile__ ( "call *%5"
126
+			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
127
+				 "+r" ( reg3 ), "+r" ( reg4 )
128
+			       : "r" ( &xen->hypercall[hypercall] )
129
+			       : XEN_REG5, "memory" );
130
+	return retval;
131
+}
132
+
133
+/**
134
+ * Issue hypercall with five arguments
135
+ *
136
+ * @v xen		Xen hypervisor
137
+ * @v hypercall		Hypercall number
138
+ * @v arg1		First argument
139
+ * @v arg2		Second argument
140
+ * @v arg3		Third argument
141
+ * @v arg4		Fourth argument
142
+ * @v arg5		Fifth argument
143
+ * @ret retval		Return value
144
+ */
145
+static inline __attribute__ (( always_inline )) unsigned long
146
+xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall,
147
+		  unsigned long arg1, unsigned long arg2, unsigned long arg3,
148
+		  unsigned long arg4, unsigned long arg5 ) {
149
+	register unsigned long reg1 asm ( XEN_REG1 ) = arg1;
150
+	register unsigned long reg2 asm ( XEN_REG2 ) = arg2;
151
+	register unsigned long reg3 asm ( XEN_REG3 ) = arg3;
152
+	register unsigned long reg4 asm ( XEN_REG4 ) = arg4;
153
+	register unsigned long reg5 asm ( XEN_REG5 ) = arg5;
154
+	unsigned long retval;
155
+
156
+	__asm__ __volatile__ ( "call *%6"
157
+			       : "=a" ( retval ), "+r" ( reg1 ), "+r" ( reg2 ),
158
+				 "+r" ( reg3 ), "+r" ( reg4 ), "+r" ( reg5 )
159
+			       : "r" ( &xen->hypercall[hypercall] )
160
+			       : "memory" );
161
+	return retval;
162
+}
163
+
164
+#endif /* _BITS_XEN_H */

+ 3
- 0
src/include/ipxe/device.h View File

@@ -60,6 +60,9 @@ struct device_description {
60 60
 /** EFI bus type */
61 61
 #define BUS_TYPE_EFI 7
62 62
 
63
+/** Xen bus type */
64
+#define BUS_TYPE_XEN 8
65
+
63 66
 /** A hardware device */
64 67
 struct device {
65 68
 	/** Name */

+ 3
- 0
src/include/ipxe/errfile.h View File

@@ -301,6 +301,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
301 301
 #define ERRFILE_memmap_settings	      ( ERRFILE_OTHER | 0x003f0000 )
302 302
 #define ERRFILE_param_cmd	      ( ERRFILE_OTHER | 0x00400000 )
303 303
 #define ERRFILE_deflate		      ( ERRFILE_OTHER | 0x00410000 )
304
+#define ERRFILE_xenstore	      ( ERRFILE_OTHER | 0x00420000 )
305
+#define ERRFILE_xenbus		      ( ERRFILE_OTHER | 0x00430000 )
306
+#define ERRFILE_xengrant	      ( ERRFILE_OTHER | 0x00440000 )
304 307
 
305 308
 /** @} */
306 309
 

+ 73
- 0
src/include/ipxe/xen.h View File

@@ -0,0 +1,73 @@
1
+#ifndef _IPXE_XEN_H
2
+#define _IPXE_XEN_H
3
+
4
+/** @file
5
+ *
6
+ * Xen interface
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+/* Define Xen interface version before including any Xen header files */
13
+#define __XEN_INTERFACE_VERSION__ 0x00040400
14
+
15
+#include <stdint.h>
16
+#include <ipxe/uaccess.h>
17
+#include <xen/xen.h>
18
+#include <xen/event_channel.h>
19
+
20
+/* Memory barrier macros used by ring.h */
21
+#define xen_mb() mb()
22
+#define xen_rmb() rmb()
23
+#define xen_wmb() wmb()
24
+
25
+struct xen_hypercall;
26
+
27
+/** A Xen grant table */
28
+struct xen_grant {
29
+	/** Grant table entries */
30
+	union grant_entry_v2 *table;
31
+	/** Number of grant table entries (must be a power of two) */
32
+	unsigned int count;
33
+	/** Number of grant table entries in use */
34
+	unsigned int used;
35
+	/** Most recently used grant reference */
36
+	unsigned int ref;
37
+};
38
+
39
+/** A XenStore */
40
+struct xen_store {
41
+	/** XenStore domain interface */
42
+	struct xenstore_domain_interface *intf;
43
+	/** Event channel */
44
+	evtchn_port_t port;
45
+};
46
+
47
+/** A Xen hypervisor */
48
+struct xen_hypervisor {
49
+	/** Hypercall table */
50
+	struct xen_hypercall *hypercall;
51
+	/** Shared info page */
52
+	struct shared_info *shared;
53
+	/** Grant table */
54
+	struct xen_grant grant;
55
+	/** XenStore */
56
+	struct xen_store store;
57
+};
58
+
59
+#include <bits/xen.h>
60
+
61
+/**
62
+ * Convert a Xen status code to an iPXE status code
63
+ *
64
+ * @v xenrc		Xen status code (negated)
65
+ * @ret rc		iPXE status code (before negation)
66
+ *
67
+ * Xen status codes are defined in the file include/xen/errno.h in the
68
+ * Xen repository.  They happen to match the Linux error codes, some
69
+ * of which can be found in our include/ipxe/errno/linux.h.
70
+ */
71
+#define EXEN( xenrc ) EPLATFORM ( EINFO_EPLATFORM, -(xenrc) )
72
+
73
+#endif /* _IPXE_XEN_H */

+ 85
- 0
src/include/ipxe/xenbus.h View File

@@ -0,0 +1,85 @@
1
+#ifndef _IPXE_XENBUS_H
2
+#define _IPXE_XENBUS_H
3
+
4
+/** @file
5
+ *
6
+ * Xen device bus
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/device.h>
13
+#include <ipxe/tables.h>
14
+#include <ipxe/xen.h>
15
+#include <xen/io/xenbus.h>
16
+
17
+/** A Xen device */
18
+struct xen_device {
19
+	/** Generic iPXE device */
20
+	struct device dev;
21
+	/** Xen hypervisor */
22
+	struct xen_hypervisor *xen;
23
+	/** XenStore key */
24
+	char *key;
25
+	/** Backend XenStore key */
26
+	char *backend;
27
+	/** Backend domain ID */
28
+	unsigned long backend_id;
29
+	/** Driver */
30
+	struct xen_driver *driver;
31
+	/** Driver-private data */
32
+	void *priv;
33
+};
34
+
35
+/** A Xen device driver */
36
+struct xen_driver {
37
+	/** Name */
38
+	const char *name;
39
+	/** Device type */
40
+	const char *type;
41
+	/** Probe device
42
+	 *
43
+	 * @v xendev		Xen device
44
+	 * @ret rc		Return status code
45
+	 */
46
+	int ( * probe ) ( struct xen_device *xendev );
47
+	/** Remove device
48
+	 *
49
+	 * @v xendev		Xen device
50
+	 */
51
+	void ( * remove ) ( struct xen_device *xendev );
52
+};
53
+
54
+/** Xen device driver table */
55
+#define XEN_DRIVERS __table ( struct xen_driver, "xen_drivers" )
56
+
57
+/** Declare a Xen device driver */
58
+#define __xen_driver __table_entry ( XEN_DRIVERS, 01 )
59
+
60
+/**
61
+ * Set Xen device driver-private data
62
+ *
63
+ * @v xendev		Xen device
64
+ * @v priv		Private data
65
+ */
66
+static inline void xen_set_drvdata ( struct xen_device *xendev, void *priv ) {
67
+	xendev->priv = priv;
68
+}
69
+
70
+/**
71
+ * Get Xen device driver-private data
72
+ *
73
+ * @v xendev		Xen device
74
+ * @ret priv		Private data
75
+ */
76
+static inline void * xen_get_drvdata ( struct xen_device *xendev ) {
77
+	return xendev->priv;
78
+}
79
+
80
+extern int xenbus_set_state ( struct xen_device *xendev, int state );
81
+extern int xenbus_backend_wait ( struct xen_device *xendev, int state );
82
+extern int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent );
83
+extern void xenbus_remove ( struct xen_hypervisor *xen, struct device *parent );
84
+
85
+#endif /* _IPXE_XENBUS_H */

+ 59
- 0
src/include/ipxe/xenevent.h View File

@@ -0,0 +1,59 @@
1
+#ifndef _IPXE_XENEVENT_H
2
+#define _IPXE_XENEVENT_H
3
+
4
+/** @file
5
+ *
6
+ * Xen events
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/xen.h>
13
+#include <xen/event_channel.h>
14
+
15
+/**
16
+ * Close event channel
17
+ *
18
+ * @v xen		Xen hypervisor
19
+ * @v close		Event descriptor
20
+ * @ret xenrc		Xen status code
21
+ */
22
+static inline __attribute__ (( always_inline )) int
23
+xenevent_close ( struct xen_hypervisor *xen, struct evtchn_close *close ) {
24
+
25
+	return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op,
26
+				 EVTCHNOP_close, virt_to_phys ( close ) );
27
+}
28
+
29
+/**
30
+ * Send event
31
+ *
32
+ * @v xen		Xen hypervisor
33
+ * @v send		Event descriptor
34
+ * @ret xenrc		Xen status code
35
+ */
36
+static inline __attribute__ (( always_inline )) int
37
+xenevent_send ( struct xen_hypervisor *xen, struct evtchn_send *send ) {
38
+
39
+	return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op,
40
+				 EVTCHNOP_send, virt_to_phys ( send ) );
41
+}
42
+
43
+/**
44
+ * Allocate an unbound event channel
45
+ *
46
+ * @v xen		Xen hypervisor
47
+ * @v alloc_unbound	Event descriptor
48
+ * @ret xenrc		Xen status code
49
+ */
50
+static inline __attribute__ (( always_inline )) int
51
+xenevent_alloc_unbound ( struct xen_hypervisor *xen,
52
+			 struct evtchn_alloc_unbound *alloc_unbound ) {
53
+
54
+	return xen_hypercall_2 ( xen, __HYPERVISOR_event_channel_op,
55
+				 EVTCHNOP_alloc_unbound,
56
+				 virt_to_phys ( alloc_unbound ) );
57
+}
58
+
59
+#endif /* _IPXE_XENEVENT_H */

+ 102
- 0
src/include/ipxe/xengrant.h View File

@@ -0,0 +1,102 @@
1
+#ifndef _IPXE_XENGRANT_H
2
+#define _IPXE_XENGRANT_H
3
+
4
+/** @file
5
+ *
6
+ * Xen grant tables
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/io.h>
14
+#include <ipxe/xen.h>
15
+#include <xen/grant_table.h>
16
+
17
+/**
18
+ * Query grant table size
19
+ *
20
+ * @v xen		Xen hypervisor
21
+ * @v size		Table size
22
+ * @ret xenrc		Xen status code
23
+ */
24
+static inline __attribute__ (( always_inline )) int
25
+xengrant_query_size ( struct xen_hypervisor *xen,
26
+		      struct gnttab_query_size *size ) {
27
+
28
+	return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op,
29
+				 GNTTABOP_query_size,
30
+				 virt_to_phys ( size ), 1 );
31
+}
32
+
33
+/**
34
+ * Set grant table version
35
+ *
36
+ * @v xen		Xen hypervisor
37
+ * @v version		Version
38
+ * @ret xenrc		Xen status code
39
+ */
40
+static inline __attribute__ (( always_inline )) int
41
+xengrant_set_version ( struct xen_hypervisor *xen,
42
+		       struct gnttab_set_version *version ) {
43
+
44
+	return xen_hypercall_3 ( xen, __HYPERVISOR_grant_table_op,
45
+				 GNTTABOP_set_version,
46
+				 virt_to_phys ( version ), 1 );
47
+}
48
+
49
+/**
50
+ * Invalidate access to a page
51
+ *
52
+ * @v xen		Xen hypervisor
53
+ * @v ref		Grant reference
54
+ */
55
+static inline __attribute__ (( always_inline )) void
56
+xengrant_invalidate ( struct xen_hypervisor *xen, grant_ref_t ref ) {
57
+	union grant_entry_v2 *entry = &xen->grant.table[ref];
58
+
59
+	/* Sanity check */
60
+	assert ( ( readw ( &entry->hdr.flags ) &
61
+		   ( GTF_reading | GTF_writing ) ) == 0 );
62
+
63
+	/* This should apparently be done using a cmpxchg instruction.
64
+	 * We omit this: partly in the interests of simplicity, but
65
+	 * mainly since our control flow generally does not permit
66
+	 * failure paths to themselves fail.
67
+	 */
68
+	writew ( 0, &entry->hdr.flags );
69
+}
70
+
71
+/**
72
+ * Permit access to a page
73
+ *
74
+ * @v xen		Xen hypervisor
75
+ * @v ref		Grant reference
76
+ * @v domid		Domain ID
77
+ * @v subflags		Additional flags
78
+ * @v page		Page start
79
+ */
80
+static inline __attribute__ (( always_inline )) void
81
+xengrant_permit_access ( struct xen_hypervisor *xen, grant_ref_t ref,
82
+			 domid_t domid, unsigned int subflags, void *page ) {
83
+	union grant_entry_v2 *entry = &xen->grant.table[ref];
84
+	unsigned long frame = ( virt_to_phys ( page ) / PAGE_SIZE );
85
+
86
+	writew ( domid, &entry->full_page.hdr.domid );
87
+	if ( sizeof ( physaddr_t ) == sizeof ( uint64_t ) ) {
88
+		writeq ( frame, &entry->full_page.frame );
89
+	} else {
90
+		writel ( frame, &entry->full_page.frame );
91
+	}
92
+	wmb();
93
+	writew ( ( GTF_permit_access | subflags ), &entry->full_page.hdr.flags);
94
+	wmb();
95
+}
96
+
97
+extern int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
98
+			    unsigned int count );
99
+extern void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
100
+			    unsigned int count );
101
+
102
+#endif /* _IPXE_XENGRANT_H */

+ 46
- 0
src/include/ipxe/xenmem.h View File

@@ -0,0 +1,46 @@
1
+#ifndef _IPXE_XENMEM_H
2
+#define _IPXE_XENMEM_H
3
+
4
+/** @file
5
+ *
6
+ * Xen memory operations
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/xen.h>
13
+#include <xen/memory.h>
14
+
15
+/**
16
+ * Add page to physical address space
17
+ *
18
+ * @v xen		Xen hypervisor
19
+ * @v add		Page mapping descriptor
20
+ * @ret xenrc		Xen status code
21
+ */
22
+static inline __attribute__ (( always_inline )) int
23
+xenmem_add_to_physmap ( struct xen_hypervisor *xen,
24
+			struct xen_add_to_physmap *add ) {
25
+
26
+	return xen_hypercall_2 ( xen, __HYPERVISOR_memory_op,
27
+				 XENMEM_add_to_physmap, virt_to_phys ( add ) );
28
+}
29
+
30
+/**
31
+ * Remove page from physical address space
32
+ *
33
+ * @v xen		Xen hypervisor
34
+ * @v remove		Page mapping descriptor
35
+ * @ret xenrc		Xen status code
36
+ */
37
+static inline __attribute__ (( always_inline )) int
38
+xenmem_remove_from_physmap ( struct xen_hypervisor *xen,
39
+			     struct xen_remove_from_physmap *remove ) {
40
+
41
+	return xen_hypercall_2 ( xen, __HYPERVISOR_memory_op,
42
+				 XENMEM_remove_from_physmap,
43
+				 virt_to_phys ( remove ) );
44
+}
45
+
46
+#endif /* _IPXE_XENMEM_H */

+ 29
- 0
src/include/ipxe/xenstore.h View File

@@ -0,0 +1,29 @@
1
+#ifndef _IPXE_XENSTORE_H
2
+#define _IPXE_XENSTORE_H
3
+
4
+/** @file
5
+ *
6
+ * XenStore interface
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/xen.h>
13
+
14
+extern __attribute__ (( sentinel )) int
15
+xenstore_read ( struct xen_hypervisor *xen, char **value, ... );
16
+extern __attribute__ (( sentinel )) int
17
+xenstore_read_num ( struct xen_hypervisor *xen, unsigned long *num, ... );
18
+extern __attribute__ (( sentinel )) int
19
+xenstore_write ( struct xen_hypervisor *xen, const char *value, ... );
20
+extern __attribute__ (( sentinel )) int
21
+xenstore_write_num ( struct xen_hypervisor *xen, unsigned long num, ... );
22
+extern __attribute__ (( sentinel )) int
23
+xenstore_rm ( struct xen_hypervisor *xen, ... );
24
+extern __attribute__ (( sentinel )) int
25
+xenstore_directory ( struct xen_hypervisor *xen, char **children, size_t *len,
26
+		     ... );
27
+extern void xenstore_dump ( struct xen_hypervisor *xen, const char *key );
28
+
29
+#endif /* _IPXE_XENSTORE_H */

+ 44
- 0
src/include/ipxe/xenver.h View File

@@ -0,0 +1,44 @@
1
+#ifndef _IPXE_XENVER_H
2
+#define _IPXE_VENVER_H
3
+
4
+/** @file
5
+ *
6
+ * Xen version
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/xen.h>
13
+#include <xen/version.h>
14
+
15
+/**
16
+ * Get Xen version
17
+ *
18
+ * @v xen		Xen hypervisor
19
+ * @ret version		Version (major.minor: 16 bits each)
20
+ */
21
+static inline __attribute__ (( always_inline )) uint32
22
+xenver_version ( struct xen_hypervisor *xen ) {
23
+
24
+	return xen_hypercall_2 ( xen, __HYPERVISOR_xen_version,
25
+				 XENVER_version, 0 );
26
+}
27
+
28
+/**
29
+ * Get Xen extra version string
30
+ *
31
+ * @v xen		Xen hypervisor
32
+ * @v extraversion	Extra version string to fill in
33
+ * @ret xenrc		Xen status code
34
+ */
35
+static inline __attribute__ (( always_inline )) int
36
+xenver_extraversion ( struct xen_hypervisor *xen,
37
+		      xen_extraversion_t *extraversion ) {
38
+
39
+	return xen_hypercall_2 ( xen, __HYPERVISOR_xen_version,
40
+				 XENVER_extraversion,
41
+				 virt_to_phys ( extraversion ) );
42
+}
43
+
44
+#endif /* _IPXE_XENVER_H */

+ 393
- 0
src/interface/xen/xenbus.c View File

@@ -0,0 +1,393 @@
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
+#include <stdio.h>
23
+#include <errno.h>
24
+#include <ipxe/malloc.h>
25
+#include <ipxe/device.h>
26
+#include <ipxe/timer.h>
27
+#include <ipxe/nap.h>
28
+#include <ipxe/xen.h>
29
+#include <ipxe/xenstore.h>
30
+#include <ipxe/xenbus.h>
31
+
32
+/** @file
33
+ *
34
+ * Xen device bus
35
+ *
36
+ */
37
+
38
+/* Disambiguate the various error causes */
39
+#define ETIMEDOUT_UNKNOWN						\
40
+	__einfo_error ( EINFO_ETIMEDOUT_UNKNOWN )
41
+#define EINFO_ETIMEDOUT_UNKNOWN						\
42
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateUnknown,		\
43
+			  "Unknown" )
44
+#define ETIMEDOUT_INITIALISING						\
45
+	__einfo_error ( EINFO_ETIMEDOUT_INITIALISING )
46
+#define EINFO_ETIMEDOUT_INITIALISING					\
47
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialising,	\
48
+			  "Initialising" )
49
+#define ETIMEDOUT_INITWAIT						\
50
+	__einfo_error ( EINFO_ETIMEDOUT_INITWAIT )
51
+#define EINFO_ETIMEDOUT_INITWAIT					\
52
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitWait,		\
53
+			  "InitWait" )
54
+#define ETIMEDOUT_INITIALISED						\
55
+	__einfo_error ( EINFO_ETIMEDOUT_INITIALISED )
56
+#define EINFO_ETIMEDOUT_INITIALISED					\
57
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateInitialised,	\
58
+			  "Initialised" )
59
+#define ETIMEDOUT_CONNECTED						\
60
+	__einfo_error ( EINFO_ETIMEDOUT_CONNECTED )
61
+#define EINFO_ETIMEDOUT_CONNECTED					\
62
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateConnected,	\
63
+			  "Connected" )
64
+#define ETIMEDOUT_CLOSING						\
65
+	__einfo_error ( EINFO_ETIMEDOUT_CLOSING )
66
+#define EINFO_ETIMEDOUT_CLOSING						\
67
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosing,		\
68
+			  "Closing" )
69
+#define ETIMEDOUT_CLOSED						\
70
+	__einfo_error ( EINFO_ETIMEDOUT_CLOSED )
71
+#define EINFO_ETIMEDOUT_CLOSED						\
72
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateClosed,		\
73
+			  "Closed" )
74
+#define ETIMEDOUT_RECONFIGURING						\
75
+	__einfo_error ( EINFO_ETIMEDOUT_RECONFIGURING )
76
+#define EINFO_ETIMEDOUT_RECONFIGURING					\
77
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfiguring,	\
78
+			  "Reconfiguring" )
79
+#define ETIMEDOUT_RECONFIGURED						\
80
+	__einfo_error ( EINFO_ETIMEDOUT_RECONFIGURED )
81
+#define EINFO_ETIMEDOUT_RECONFIGURED					\
82
+	__einfo_uniqify ( EINFO_ETIMEDOUT, XenbusStateReconfigured,	\
83
+			  "Reconfigured" )
84
+#define ETIMEDOUT_STATE( state )					\
85
+	EUNIQ ( EINFO_ETIMEDOUT, (state), ETIMEDOUT_UNKNOWN,		\
86
+		ETIMEDOUT_INITIALISING, ETIMEDOUT_INITWAIT,		\
87
+		ETIMEDOUT_INITIALISED, ETIMEDOUT_CONNECTED,		\
88
+		ETIMEDOUT_CLOSING, ETIMEDOUT_CLOSED,			\
89
+		ETIMEDOUT_RECONFIGURING, ETIMEDOUT_RECONFIGURED )
90
+
91
+/** Maximum time to wait for backend to reach a given state, in ticks */
92
+#define XENBUS_BACKEND_TIMEOUT ( 5 * TICKS_PER_SEC )
93
+
94
+/**
95
+ * Set device state
96
+ *
97
+ * @v xendev		Xen device
98
+ * @v state		New state
99
+ * @ret rc		Return status code
100
+ */
101
+int xenbus_set_state ( struct xen_device *xendev, int state ) {
102
+	int rc;
103
+
104
+	/* Attempt to set state */
105
+	if ( ( rc = xenstore_write_num ( xendev->xen, state, xendev->key,
106
+					 "state", NULL ) ) != 0 ) {
107
+		DBGC ( xendev, "XENBUS %s could not set state=\"%d\": %s\n",
108
+		       xendev->key, state, strerror ( rc ) );
109
+		return rc;
110
+	}
111
+
112
+	return 0;
113
+}
114
+
115
+/**
116
+ * Get backend state
117
+ *
118
+ * @v xendev		Xen device
119
+ * @ret state		Backend state, or negative error
120
+ */
121
+static int xenbus_backend_state ( struct xen_device *xendev ) {
122
+	unsigned long state;
123
+	int rc;
124
+
125
+	/* Attempt to get backend state */
126
+	if ( ( rc = xenstore_read_num ( xendev->xen, &state, xendev->backend,
127
+					"state", NULL ) ) != 0 ) {
128
+		DBGC ( xendev, "XENBUS %s could not read %s/state: %s\n",
129
+		       xendev->key, xendev->backend, strerror ( rc ) );
130
+		return rc;
131
+	}
132
+
133
+	return state;
134
+}
135
+
136
+/**
137
+ * Wait for backend to reach a given state
138
+ *
139
+ * @v xendev		Xen device
140
+ * @v state		Desired backend state
141
+ * @ret rc		Return status code
142
+ */
143
+int xenbus_backend_wait ( struct xen_device *xendev, int state ) {
144
+	unsigned long started = currticks();
145
+	unsigned long elapsed;
146
+	unsigned int attempts = 0;
147
+	int current_state;
148
+	int rc;
149
+
150
+	/* Wait for backend to reach this state */
151
+	do {
152
+
153
+		/* Get current backend state */
154
+		current_state = xenbus_backend_state ( xendev );
155
+		if ( current_state < 0 ) {
156
+			rc = current_state;
157
+			return rc;
158
+		}
159
+		if ( current_state == state )
160
+			return 0;
161
+
162
+		/* Allow time for backend to react */
163
+		cpu_nap();
164
+
165
+		/* XenStore is a very slow interface; any fixed delay
166
+		 * time would be dwarfed by the XenStore access time.
167
+		 * We therefore use wall clock to time out this
168
+		 * operation.
169
+		 */
170
+		elapsed = ( currticks() - started );
171
+		attempts++;
172
+
173
+	} while ( elapsed < XENBUS_BACKEND_TIMEOUT );
174
+
175
+	/* Construct status code from current backend state */
176
+	rc = -ETIMEDOUT_STATE ( current_state );
177
+	DBGC ( xendev, "XENBUS %s timed out after %d attempts waiting for "
178
+	       "%s/state=\"%d\": %s\n", xendev->key, attempts, xendev->backend,
179
+	       state, strerror ( rc ) );
180
+
181
+	return rc;
182
+}
183
+
184
+/**
185
+ * Find driver for Xen device
186
+ *
187
+ * @v type		Device type
188
+ * @ret driver		Driver, or NULL
189
+ */
190
+static struct xen_driver * xenbus_find_driver ( const char *type ) {
191
+	struct xen_driver *xendrv;
192
+
193
+	for_each_table_entry ( xendrv, XEN_DRIVERS ) {
194
+		if ( strcmp ( xendrv->type, type ) == 0 )
195
+			return xendrv;
196
+	}
197
+	return NULL;
198
+}
199
+
200
+/**
201
+ * Probe Xen device
202
+ *
203
+ * @v xen		Xen hypervisor
204
+ * @v parent		Parent device
205
+ * @v type		Device type
206
+ * @v instance		Device instance
207
+ * @ret rc		Return status code
208
+ */
209
+static int xenbus_probe_device ( struct xen_hypervisor *xen,
210
+				 struct device *parent, const char *type,
211
+				 const char *instance ) {
212
+	struct xen_device *xendev;
213
+	size_t key_len;
214
+	int rc;
215
+
216
+	/* Allocate and initialise structure */
217
+	key_len = ( 7 /* "device/" */ + strlen ( type ) + 1 /* "/" */ +
218
+		    strlen ( instance ) + 1 /* NUL */ );
219
+	xendev = zalloc ( sizeof ( *xendev ) + key_len );
220
+	if ( ! xendev ) {
221
+		rc = -ENOMEM;
222
+		goto err_alloc;
223
+	}
224
+	snprintf ( xendev->dev.name, sizeof ( xendev->dev.name ), "%s/%s",
225
+		   type, instance );
226
+	xendev->dev.desc.bus_type = BUS_TYPE_XEN;
227
+	INIT_LIST_HEAD ( &xendev->dev.children );
228
+	list_add_tail ( &xendev->dev.siblings, &parent->children );
229
+	xendev->dev.parent = parent;
230
+	xendev->xen = xen;
231
+	xendev->key = ( ( void * ) ( xendev + 1 ) );
232
+	snprintf ( xendev->key, key_len, "device/%s/%s", type, instance );
233
+
234
+	/* Read backend key */
235
+	if ( ( rc = xenstore_read ( xen, &xendev->backend, xendev->key,
236
+				    "backend", NULL ) ) != 0 ) {
237
+		DBGC ( xendev, "XENBUS %s could not read backend: %s\n",
238
+		       xendev->key, strerror ( rc ) );
239
+		goto err_read_backend;
240
+	}
241
+
242
+	/* Read backend domain ID */
243
+	if ( ( rc = xenstore_read_num ( xen, &xendev->backend_id, xendev->key,
244
+					"backend-id", NULL ) ) != 0 ) {
245
+		DBGC ( xendev, "XENBUS %s could not read backend-id: %s\n",
246
+		       xendev->key, strerror ( rc ) );
247
+		goto err_read_backend_id;
248
+	}
249
+	DBGC ( xendev, "XENBUS %s backend=\"%s\" in domain %ld\n",
250
+	       xendev->key, xendev->backend, xendev->backend_id );
251
+
252
+	/* Look for a driver */
253
+	xendev->driver = xenbus_find_driver ( type );
254
+	if ( ! xendev->driver ) {
255
+		DBGC ( xendev, "XENBUS %s has no driver\n", xendev->key );
256
+		/* Not a fatal error */
257
+		rc = 0;
258
+		goto err_no_driver;
259
+	}
260
+	xendev->dev.driver_name = xendev->driver->name;
261
+	DBGC ( xendev, "XENBUS %s has driver \"%s\"\n", xendev->key,
262
+	       xendev->driver->name );
263
+
264
+	/* Probe driver */
265
+	if ( ( rc = xendev->driver->probe ( xendev ) ) != 0 ) {
266
+		DBGC ( xendev, "XENBUS could not probe %s: %s\n",
267
+		       xendev->key, strerror ( rc ) );
268
+		goto err_probe;
269
+	}
270
+
271
+	return 0;
272
+
273
+	xendev->driver->remove ( xendev );
274
+ err_probe:
275
+ err_no_driver:
276
+ err_read_backend_id:
277
+	free ( xendev->backend );
278
+ err_read_backend:
279
+	list_del ( &xendev->dev.siblings );
280
+	free ( xendev );
281
+ err_alloc:
282
+	return rc;
283
+}
284
+
285
+/**
286
+ * Remove Xen device
287
+ *
288
+ * @v xendev		Xen device
289
+ */
290
+static void xenbus_remove_device ( struct xen_device *xendev ) {
291
+
292
+	/* Remove device */
293
+	xendev->driver->remove ( xendev );
294
+	free ( xendev->backend );
295
+	list_del ( &xendev->dev.siblings );
296
+	free ( xendev );
297
+}
298
+
299
+/**
300
+ * Probe Xen devices of a given type
301
+ *
302
+ * @v xen		Xen hypervisor
303
+ * @v parent		Parent device
304
+ * @v type		Device type
305
+ * @ret rc		Return status code
306
+ */
307
+static int xenbus_probe_type ( struct xen_hypervisor *xen,
308
+			       struct device *parent, const char *type ) {
309
+	char *children;
310
+	char *child;
311
+	size_t len;
312
+	int rc;
313
+
314
+	/* Get children of this key */
315
+	if ( ( rc = xenstore_directory ( xen, &children, &len, "device",
316
+					 type, NULL ) ) != 0 ) {
317
+		DBGC ( xen, "XENBUS could not list \"%s\" devices: %s\n",
318
+		       type, strerror ( rc ) );
319
+		goto err_directory;
320
+	}
321
+
322
+	/* Probe each child */
323
+	for ( child = children ; child < ( children + len ) ;
324
+	      child += ( strlen ( child ) + 1 /* NUL */ ) ) {
325
+		if ( ( rc = xenbus_probe_device ( xen, parent, type,
326
+						  child ) ) != 0 )
327
+			goto err_probe_device;
328
+	}
329
+
330
+	free ( children );
331
+	return 0;
332
+
333
+ err_probe_device:
334
+	free ( children );
335
+ err_directory:
336
+	return rc;
337
+}
338
+
339
+/**
340
+ * Probe Xen bus
341
+ *
342
+ * @v xen		Xen hypervisor
343
+ * @v parent		Parent device
344
+ * @ret rc		Return status code
345
+ */
346
+int xenbus_probe ( struct xen_hypervisor *xen, struct device *parent ) {
347
+	char *types;
348
+	char *type;
349
+	size_t len;
350
+	int rc;
351
+
352
+	/* Get children of "device" key */
353
+	if ( ( rc = xenstore_directory ( xen, &types, &len, "device",
354
+					 NULL ) ) != 0 ) {
355
+		DBGC ( xen, "XENBUS could not list device types: %s\n",
356
+		       strerror ( rc ) );
357
+		goto err_directory;
358
+	}
359
+
360
+	/* Probe each child type */
361
+	for ( type = types ; type < ( types + len ) ;
362
+	      type += ( strlen ( type ) + 1 /* NUL */ ) ) {
363
+		if ( ( rc = xenbus_probe_type ( xen, parent, type ) ) != 0 )
364
+			goto err_probe_type;
365
+	}
366
+
367
+	free ( types );
368
+	return 0;
369
+
370
+	xenbus_remove ( xen, parent );
371
+ err_probe_type:
372
+	free ( types );
373
+ err_directory:
374
+	return rc;
375
+}
376
+
377
+/**
378
+ * Remove Xen bus
379
+ *
380
+ * @v xen		Xen hypervisor
381
+ * @v parent		Parent device
382
+ */
383
+void xenbus_remove ( struct xen_hypervisor *xen __unused,
384
+		     struct device *parent ) {
385
+	struct xen_device *xendev;
386
+	struct xen_device *tmp;
387
+
388
+	/* Remove devices */
389
+	list_for_each_entry_safe ( xendev, tmp, &parent->children,
390
+				   dev.siblings ) {
391
+		xenbus_remove_device ( xendev );
392
+	}
393
+}

+ 125
- 0
src/interface/xen/xengrant.c View File

@@ -0,0 +1,125 @@
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
+#include <stdint.h>
23
+#include <errno.h>
24
+#include <assert.h>
25
+#include <ipxe/io.h>
26
+#include <ipxe/xen.h>
27
+#include <ipxe/xengrant.h>
28
+
29
+/** @file
30
+ *
31
+ * Xen grant tables
32
+ *
33
+ */
34
+
35
+/**
36
+ * Allocate grant references
37
+ *
38
+ * @v xen		Xen hypervisor
39
+ * @v refs		Grant references to fill in
40
+ * @v count		Number of references
41
+ * @ret rc		Return status code
42
+ */
43
+int xengrant_alloc ( struct xen_hypervisor *xen, grant_ref_t *refs,
44
+		     unsigned int count ) {
45
+	union grant_entry_v2 *entry;
46
+	unsigned int mask = ( xen->grant.count - 1 );
47
+	unsigned int check = 0;
48
+	unsigned int avail;
49
+	unsigned int ref;
50
+
51
+	/* Fail unless we have enough references available */
52
+	avail = ( xen->grant.count - xen->grant.used -
53
+		  GNTTAB_NR_RESERVED_ENTRIES );
54
+	if ( avail < count ) {
55
+		DBGC ( xen, "XENGRANT cannot allocate %d references (only %d "
56
+		       "of %d available)\n", count, avail, xen->grant.count );
57
+		return -ENOBUFS;
58
+	}
59
+	DBGC ( xen, "XENGRANT allocating %d references (from %d of %d "
60
+	       "available)\n", count, avail, xen->grant.count );
61
+
62
+	/* Update number of references used */
63
+	xen->grant.used += count;
64
+
65
+	/* Find unused references */
66
+	for ( ref = xen->grant.ref ; count ; ref = ( ( ref + 1 ) & mask ) ) {
67
+
68
+		/* Sanity check */
69
+		assert ( check++ < xen->grant.count );
70
+
71
+		/* Skip reserved references */
72
+		if ( ref < GNTTAB_NR_RESERVED_ENTRIES )
73
+			continue;
74
+
75
+		/* Skip in-use references */
76
+		entry = &xen->grant.table[ref];
77
+		if ( readw ( &entry->hdr.flags ) & GTF_type_mask )
78
+			continue;
79
+		if ( readw ( &entry->hdr.domid ) == DOMID_SELF )
80
+			continue;
81
+
82
+		/* Mark reference as in-use.  We leave the flags as
83
+		 * empty (to avoid creating a valid grant table entry)
84
+		 * and set the domid to DOMID_SELF.
85
+		 */
86
+		writew ( DOMID_SELF, &entry->hdr.domid );
87
+		DBGC2 ( xen, "XENGRANT allocated ref %d\n", ref );
88
+
89
+		/* Record reference */
90
+		refs[--count] = ref;
91
+	}
92
+
93
+	/* Update cursor */
94
+	xen->grant.ref = ref;
95
+
96
+	return 0;
97
+}
98
+
99
+/**
100
+ * Free grant references
101
+ *
102
+ * @v xen		Xen hypervisor
103
+ * @v refs		Grant references
104
+ * @v count		Number of references
105
+ */
106
+void xengrant_free ( struct xen_hypervisor *xen, grant_ref_t *refs,
107
+		     unsigned int count ) {
108
+	union grant_entry_v2 *entry;
109
+	unsigned int ref;
110
+	unsigned int i;
111
+
112
+	/* Free references */
113
+	for ( i = 0 ; i < count ; i++ ) {
114
+
115
+		/* Sanity check */
116
+		ref = refs[i];
117
+		assert ( ref < xen->grant.count );
118
+
119
+		/* Mark reference as unused */
120
+		entry = &xen->grant.table[ref];
121
+		writew ( 0, &entry->hdr.flags );
122
+		writew ( 0, &entry->hdr.domid );
123
+		DBGC2 ( xen, "XENGRANT freed ref %d\n", ref );
124
+	}
125
+}

+ 547
- 0
src/interface/xen/xenstore.c View File

@@ -0,0 +1,547 @@
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
+#include <stdint.h>
23
+#include <stdarg.h>
24
+#include <stdlib.h>
25
+#include <stdio.h>
26
+#include <string.h>
27
+#include <ipxe/io.h>
28
+#include <ipxe/nap.h>
29
+#include <ipxe/malloc.h>
30
+#include <ipxe/xen.h>
31
+#include <ipxe/xenevent.h>
32
+#include <ipxe/xenstore.h>
33
+
34
+/*
35
+ * xs_wire.h attempts to define a static error table xsd_errors, which
36
+ * interacts badly with the dynamically generated error numbers used
37
+ * by iPXE.  Prevent this table from being constructed by including
38
+ * errno.h only after including xs_wire.h.
39
+ *
40
+ */
41
+#include <xen/io/xs_wire.h>
42
+#include <errno.h>
43
+
44
+/** @file
45
+ *
46
+ * XenStore interface
47
+ *
48
+ */
49
+
50
+/** Request identifier */
51
+static uint32_t xenstore_req_id;
52
+
53
+/**
54
+ * Send XenStore request raw data
55
+ *
56
+ * @v xen		Xen hypervisor
57
+ * @v data		Data buffer
58
+ * @v len		Length of data
59
+ */
60
+static void xenstore_send ( struct xen_hypervisor *xen, const void *data,
61
+			    size_t len ) {
62
+	struct xenstore_domain_interface *intf = xen->store.intf;
63
+	XENSTORE_RING_IDX prod = readl ( &intf->req_prod );
64
+	XENSTORE_RING_IDX cons;
65
+	XENSTORE_RING_IDX idx;
66
+	const char *bytes = data;
67
+	size_t offset = 0;
68
+	size_t fill;
69
+
70
+	DBGCP ( intf, "XENSTORE raw request:\n" );
71
+	DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( prod ), data, len );
72
+
73
+	/* Write one byte at a time */
74
+	while ( offset < len ) {
75
+
76
+		/* Wait for space to become available */
77
+		while ( 1 ) {
78
+			cons = readl ( &intf->req_cons );
79
+			fill = ( prod - cons );
80
+			if ( fill < XENSTORE_RING_SIZE )
81
+				break;
82
+			DBGC2 ( xen, "." );
83
+			cpu_nap();
84
+			rmb();
85
+		}
86
+
87
+		/* Write byte */
88
+		idx = MASK_XENSTORE_IDX ( prod++ );
89
+		writeb ( bytes[offset++], &intf->req[idx] );
90
+	}
91
+
92
+	/* Update producer counter */
93
+	wmb();
94
+	writel ( prod, &intf->req_prod );
95
+	wmb();
96
+}
97
+
98
+/**
99
+ * Send XenStore request string (excluding terminating NUL)
100
+ *
101
+ * @v xen		Xen hypervisor
102
+ * @v string		String
103
+ */
104
+static void xenstore_send_string ( struct xen_hypervisor *xen,
105
+				   const char *string ) {
106
+
107
+	xenstore_send ( xen, string, strlen ( string ) );
108
+}
109
+
110
+/**
111
+ * Receive XenStore response raw data
112
+ *
113
+ * @v xen		Xen hypervisor
114
+ * @v data		Data buffer, or NULL to discard data
115
+ * @v len		Length of data
116
+ */
117
+static void xenstore_recv ( struct xen_hypervisor *xen, void *data,
118
+			    size_t len ) {
119
+	struct xenstore_domain_interface *intf = xen->store.intf;
120
+	XENSTORE_RING_IDX cons = readl ( &intf->rsp_cons );
121
+	XENSTORE_RING_IDX prod;
122
+	XENSTORE_RING_IDX idx;
123
+	char *bytes = data;
124
+	size_t offset = 0;
125
+	size_t fill;
126
+
127
+	DBGCP ( intf, "XENSTORE raw response:\n" );
128
+
129
+	/* Read one byte at a time */
130
+	while ( offset < len ) {
131
+
132
+		/* Wait for data to be ready */
133
+		while ( 1 ) {
134
+			prod = readl ( &intf->rsp_prod );
135
+			fill = ( prod - cons );
136
+			if ( fill > 0 )
137
+				break;
138
+			DBGC2 ( xen, "." );
139
+			cpu_nap();
140
+			rmb();
141
+		}
142
+
143
+		/* Read byte */
144
+		idx = MASK_XENSTORE_IDX ( cons++ );
145
+		if ( data )
146
+			bytes[offset++] = readb ( &intf->rsp[idx] );
147
+	}
148
+	if ( data )
149
+		DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( cons - len ), data, len );
150
+
151
+	/* Update consumer counter */
152
+	writel ( cons, &intf->rsp_cons );
153
+	wmb();
154
+}
155
+
156
+/**
157
+ * Send XenStore request
158
+ *
159
+ * @v xen		Xen hypervisor
160
+ * @v type		Message type
161
+ * @v req_id		Request ID
162
+ * @v value		Value, or NULL to omit
163
+ * @v key		Key path components
164
+ * @ret rc		Return status code
165
+ */
166
+static int xenstore_request ( struct xen_hypervisor *xen,
167
+			      enum xsd_sockmsg_type type, uint32_t req_id,
168
+			      const char *value, va_list key ) {
169
+	struct xsd_sockmsg msg;
170
+	struct evtchn_send event;
171
+	const char *string;
172
+	va_list tmp;
173
+	int xenrc;
174
+	int rc;
175
+
176
+	/* Construct message header */
177
+	msg.type = type;
178
+	msg.req_id = req_id;
179
+	msg.tx_id = 0;
180
+	msg.len = 0;
181
+	DBGC2 ( xen, "XENSTORE request ID %d type %d ", req_id, type );
182
+
183
+	/* Calculate total length */
184
+	va_copy ( tmp, key );
185
+	while ( ( string = va_arg ( tmp, const char * ) ) != NULL ) {
186
+		DBGC2 ( xen, "%s%s", ( msg.len ? "/" : "" ), string );
187
+		msg.len += ( strlen ( string ) + 1 /* '/' or NUL */ );
188
+	}
189
+	va_end ( tmp );
190
+	if ( value ) {
191
+		DBGC2 ( xen, " = \"%s\"", value );
192
+		msg.len += strlen ( value );
193
+	}
194
+	DBGC2 ( xen, "\n" );
195
+
196
+	/* Send message */
197
+	xenstore_send ( xen, &msg, sizeof ( msg ) );
198
+	string = va_arg ( key, const char * );
199
+	assert ( string != NULL );
200
+	xenstore_send_string ( xen, string );
201
+	while ( ( string = va_arg ( key, const char * ) ) != NULL ) {
202
+		xenstore_send_string ( xen, "/" );
203
+		xenstore_send_string ( xen, string );
204
+	}
205
+	xenstore_send ( xen, "", 1 ); /* Separating NUL */
206
+	if ( value )
207
+		xenstore_send_string ( xen, value );
208
+
209
+	/* Notify the back end */
210
+	event.port = xen->store.port;
211
+	if ( ( xenrc = xenevent_send ( xen, &event ) ) != 0 ) {
212
+		rc = -EXEN ( xenrc );
213
+		DBGC ( xen, "XENSTORE could not notify back end: %s\n",
214
+		       strerror ( rc ) );
215
+		return rc;
216
+	}
217
+
218
+	return 0;
219
+}
220
+
221
+/**
222
+ * Receive XenStore response
223
+ *
224
+ * @v xen		Xen hypervisor
225
+ * @v req_id		Request ID
226
+ * @v value		Value to fill in
227
+ * @v len		Length to fill in
228
+ * @ret rc		Return status code
229
+ *
230
+ * The caller is responsible for eventually calling free() on the
231
+ * returned value.  Note that the value may comprise multiple
232
+ * NUL-terminated strings concatenated together.  A terminating NUL
233
+ * will always be appended to the returned value.
234
+ */
235
+static int xenstore_response ( struct xen_hypervisor *xen, uint32_t req_id,
236
+			       char **value, size_t *len ) {
237
+	struct xsd_sockmsg msg;
238
+	char *string;
239
+	int rc;
240
+
241
+	/* Receive message header */
242
+	xenstore_recv ( xen, &msg, sizeof ( msg ) );
243
+	*len = msg.len;
244
+
245
+	/* Allocate space for response */
246
+	*value = zalloc ( msg.len + 1 /* terminating NUL */ );
247
+
248
+	/* Receive data.  Do this even if allocation failed, or if the
249
+	 * request ID was incorrect, to avoid leaving data in the
250
+	 * ring.
251
+	 */
252
+	xenstore_recv ( xen, *value, msg.len );
253
+
254
+	/* Validate request ID */
255
+	if ( msg.req_id != req_id ) {
256
+		DBGC ( xen, "XENSTORE response ID mismatch (got %d, expected "
257
+		       "%d)\n", msg.req_id, req_id );
258
+		rc = -EPROTO;
259
+		goto err_req_id;
260
+	}
261
+
262
+	/* Check for allocation failure */
263
+	if ( ! *value ) {
264
+		DBGC ( xen, "XENSTORE could not allocate %d bytes for "
265
+		       "response\n", msg.len );
266
+		rc = -ENOMEM;
267
+		goto err_alloc;
268
+	}
269
+
270
+	/* Check for explicit errors */
271
+	if ( msg.type == XS_ERROR ) {
272
+		DBGC ( xen, "XENSTORE response error \"%s\"\n", *value );
273
+		rc = -EIO;
274
+		goto err_explicit;
275
+	}
276
+
277
+	DBGC2 ( xen, "XENSTORE response ID %d\n", req_id );
278
+	if ( DBG_EXTRA ) {
279
+		for ( string = *value ; string < ( *value + msg.len ) ;
280
+		      string += ( strlen ( string ) + 1 /* NUL */ ) ) {
281
+			DBGC2 ( xen, " - \"%s\"\n", string );
282
+		}
283
+	}
284
+	return 0;
285
+
286
+ err_explicit:
287
+ err_alloc:
288
+ err_req_id:
289
+	free ( *value );
290
+	*value = NULL;
291
+	return rc;
292
+}
293
+
294
+/**
295
+ * Issue a XenStore message
296
+ *
297
+ * @v xen		Xen hypervisor
298
+ * @v type		Message type
299
+ * @v response		Response value to fill in, or NULL to discard
300
+ * @v len		Response length to fill in, or NULL to ignore
301
+ * @v request		Request value, or NULL to omit
302
+ * @v key		Key path components
303
+ * @ret rc		Return status code
304
+ */
305
+static int xenstore_message ( struct xen_hypervisor *xen,
306
+			      enum xsd_sockmsg_type type, char **response,
307
+			      size_t *len, const char *request, va_list key ) {
308
+	char *response_value;
309
+	size_t response_len;
310
+	int rc;
311
+
312
+	/* Send request */
313
+	if ( ( rc = xenstore_request ( xen, type, ++xenstore_req_id,
314
+				       request, key ) ) != 0 )
315
+		return rc;
316
+
317
+	/* Receive response */
318
+	if ( ( rc = xenstore_response ( xen, xenstore_req_id, &response_value,
319
+					&response_len ) ) != 0 )
320
+		return rc;
321
+
322
+	/* Return response, if applicable */
323
+	if ( response ) {
324
+		*response = response_value;
325
+	} else {
326
+		free ( response_value );
327
+	}
328
+	if ( len )
329
+		*len = response_len;
330
+
331
+	return 0;
332
+}
333
+
334
+/**
335
+ * Read XenStore value
336
+ *
337
+ * @v xen		Xen hypervisor
338
+ * @v value		Value to fill in
339
+ * @v key		Key path components
340
+ * @ret rc		Return status code
341
+ *
342
+ * On a successful return, the caller is responsible for calling
343
+ * free() on the returned value.
344
+ */
345
+static int xenstore_vread ( struct xen_hypervisor *xen, char **value,
346
+			    va_list key ) {
347
+
348
+	return xenstore_message ( xen, XS_READ, value, NULL, NULL, key );
349
+}
350
+
351
+/**
352
+ * Read XenStore value
353
+ *
354
+ * @v xen		Xen hypervisor
355
+ * @v value		Value to fill in
356
+ * @v ...		Key path components
357
+ * @ret rc		Return status code
358
+ *
359
+ * On a successful return, the caller is responsible for calling
360
+ * free() on the returned value.
361
+ */
362
+__attribute__ (( sentinel )) int
363
+xenstore_read ( struct xen_hypervisor *xen, char **value, ... ) {
364
+	va_list key;
365
+	int rc;
366
+
367
+	va_start ( key, value );
368
+	rc = xenstore_vread ( xen, value, key );
369
+	va_end ( key );
370
+	return rc;
371
+}
372
+
373
+/**
374
+ * Read XenStore numeric value
375
+ *
376
+ * @v xen		Xen hypervisor
377
+ * @v num		Numeric value to fill in
378
+ * @v ...		Key path components
379
+ * @ret rc		Return status code
380
+ */
381
+__attribute__ (( sentinel )) int
382
+xenstore_read_num ( struct xen_hypervisor *xen, unsigned long *num, ... ) {
383
+	va_list key;
384
+	char *value;
385
+	char *endp;
386
+	int rc;
387
+
388
+	/* Try to read text value */
389
+	va_start ( key, num );
390
+	rc = xenstore_vread ( xen, &value, key );
391
+	va_end ( key );
392
+	if ( rc != 0 )
393
+		goto err_read;
394
+
395
+	/* Try to parse as numeric value */
396
+	*num = strtoul ( value, &endp, 10 );
397
+	if ( ( *value == '\0' ) || ( *endp != '\0' ) ) {
398
+		DBGC ( xen, "XENSTORE found invalid numeric value \"%s\"\n",
399
+		       value );
400
+		rc = -EINVAL;
401
+		goto err_strtoul;
402
+	}
403
+
404
+ err_strtoul:
405
+	free ( value );
406
+ err_read:
407
+	return rc;
408
+}
409
+
410
+/**
411
+ * Write XenStore value
412
+ *
413
+ * @v xen		Xen hypervisor
414
+ * @v value		Value
415
+ * @v key		Key path components
416
+ * @ret rc		Return status code
417
+ */
418
+static int xenstore_vwrite ( struct xen_hypervisor *xen, const char *value,
419
+			     va_list key ) {
420
+
421
+	return xenstore_message ( xen, XS_WRITE, NULL, NULL, value, key );
422
+}
423
+
424
+/**
425
+ * Write XenStore value
426
+ *
427
+ * @v xen		Xen hypervisor
428
+ * @v value		Value
429
+ * @v ...		Key path components
430
+ * @ret rc		Return status code
431
+ */
432
+__attribute__ (( sentinel )) int
433
+xenstore_write ( struct xen_hypervisor *xen, const char *value, ... ) {
434
+	va_list key;
435
+	int rc;
436
+
437
+	va_start ( key, value );
438
+	rc = xenstore_vwrite ( xen, value, key );
439
+	va_end ( key );
440
+	return rc;
441
+}
442
+
443
+/**
444
+ * Write XenStore numeric value
445
+ *
446
+ * @v xen		Xen hypervisor
447
+ * @v num		Numeric value
448
+ * @v ...		Key path components
449
+ * @ret rc		Return status code
450
+ */
451
+__attribute__ (( sentinel )) int
452
+xenstore_write_num ( struct xen_hypervisor *xen, unsigned long num, ... ) {
453
+	char value[ 21 /* "18446744073709551615" + NUL */ ];
454
+	va_list key;
455
+	int rc;
456
+
457
+	/* Construct value */
458
+	snprintf ( value, sizeof ( value ), "%ld", num );
459
+
460
+	/* Write value */
461
+	va_start ( key, num );
462
+	rc = xenstore_vwrite ( xen, value, key );
463
+	va_end ( key );
464
+	return rc;
465
+}
466
+
467
+/**
468
+ * Delete XenStore value
469
+ *
470
+ * @v xen		Xen hypervisor
471
+ * @v ...		Key path components
472
+ * @ret rc		Return status code
473
+ */
474
+__attribute__ (( sentinel )) int
475
+xenstore_rm ( struct xen_hypervisor *xen, ... ) {
476
+	va_list key;
477
+	int rc;
478
+
479
+	va_start ( key, xen );
480
+	rc = xenstore_message ( xen, XS_RM, NULL, NULL, NULL, key );
481
+	va_end ( key );
482
+	return rc;
483
+}
484
+
485
+/**
486
+ * Read XenStore directory
487
+ *
488
+ * @v xen		Xen hypervisor
489
+ * @v children		Child key names to fill in
490
+ * @v len		Length of child key names to fill in
491
+ * @v ...		Key path components
492
+ * @ret rc		Return status code
493
+ */
494
+__attribute__ (( sentinel )) int
495
+xenstore_directory ( struct xen_hypervisor *xen, char **children, size_t *len,
496
+		     ... ) {
497
+	va_list key;
498
+	int rc;
499
+
500
+	va_start ( key, len );
501
+	rc = xenstore_message ( xen, XS_DIRECTORY, children, len, NULL, key );
502
+	va_end ( key );
503
+	return rc;
504
+}
505
+
506
+/**
507
+ * Dump XenStore directory contents (for debugging)
508
+ *
509
+ * @v xen		Xen hypervisor
510
+ * @v key		Key
511
+ */
512
+void xenstore_dump ( struct xen_hypervisor *xen, const char *key ) {
513
+	char *value;
514
+	char *children;
515
+	char *child;
516
+	char *child_key;
517
+	size_t len;
518
+	int rc;
519
+
520
+	/* Try to dump current key as a value */
521
+	if ( ( rc = xenstore_read ( xen, &value, key, NULL ) ) == 0 ) {
522
+		DBGC ( xen, "%s = \"%s\"\n", key, value );
523
+		free ( value );
524
+	}
525
+
526
+	/* Try to recurse into each child in turn */
527
+	if ( ( rc = xenstore_directory ( xen, &children, &len, key,
528
+					 NULL ) ) == 0 ) {
529
+		for ( child = children ; child < ( children + len ) ;
530
+		      child += ( strlen ( child ) + 1 /* NUL */ ) ) {
531
+
532
+			/* Construct child key */
533
+			asprintf ( &child_key, "%s/%s", key, child );
534
+			if ( ! child_key ) {
535
+				DBGC ( xen, "XENSTORE could not allocate child "
536
+				       "key \"%s/%s\"\n", key, child );
537
+				rc = -ENOMEM;
538
+				break;
539
+			}
540
+
541
+			/* Recurse into child key, continuing on error */
542
+			xenstore_dump ( xen, child_key );
543
+			free ( child_key );
544
+		}
545
+		free ( children );
546
+	}
547
+}

Loading…
Cancel
Save