1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
|
# -*- coding: utf-8 -*-
"""
Libxc Migration v2 streams
Record structures as per docs/specs/libxc-migration-stream.pandoc, and
verification routines.
"""
import sys
from struct import calcsize, unpack
from xen.migration.verify import StreamError, RecordError, VerifyBase
# Image Header
IHDR_FORMAT = "!QIIHHI"
IHDR_MARKER = 0xffffffffffffffff
IHDR_IDENT = 0x58454E46 # "XENF" in ASCII
IHDR_OPT_BIT_ENDIAN = 0
IHDR_OPT_LE = (0 << IHDR_OPT_BIT_ENDIAN)
IHDR_OPT_BE = (1 << IHDR_OPT_BIT_ENDIAN)
IHDR_OPT_RESZ_MASK = 0xfffe
# Domain Header
DHDR_FORMAT = "IHHII"
DHDR_TYPE_x86_pv = 0x00000001
DHDR_TYPE_x86_hvm = 0x00000002
dhdr_type_to_str = {
DHDR_TYPE_x86_pv : "x86 PV",
DHDR_TYPE_x86_hvm : "x86 HVM",
}
# Records
RH_FORMAT = "II"
REC_TYPE_end = 0x00000000
REC_TYPE_page_data = 0x00000001
REC_TYPE_x86_pv_info = 0x00000002
REC_TYPE_x86_pv_p2m_frames = 0x00000003
REC_TYPE_x86_pv_vcpu_basic = 0x00000004
REC_TYPE_x86_pv_vcpu_extended = 0x00000005
REC_TYPE_x86_pv_vcpu_xsave = 0x00000006
REC_TYPE_shared_info = 0x00000007
REC_TYPE_tsc_info = 0x00000008
REC_TYPE_hvm_context = 0x00000009
REC_TYPE_hvm_params = 0x0000000a
REC_TYPE_toolstack = 0x0000000b
REC_TYPE_x86_pv_vcpu_msrs = 0x0000000c
REC_TYPE_verify = 0x0000000d
REC_TYPE_checkpoint = 0x0000000e
REC_TYPE_checkpoint_dirty_pfn_list = 0x0000000f
REC_TYPE_static_data_end = 0x00000010
REC_TYPE_x86_cpuid_policy = 0x00000011
REC_TYPE_x86_msr_policy = 0x00000012
rec_type_to_str = {
REC_TYPE_end : "End",
REC_TYPE_page_data : "Page data",
REC_TYPE_x86_pv_info : "x86 PV info",
REC_TYPE_x86_pv_p2m_frames : "x86 PV P2M frames",
REC_TYPE_x86_pv_vcpu_basic : "x86 PV vcpu basic",
REC_TYPE_x86_pv_vcpu_extended : "x86 PV vcpu extended",
REC_TYPE_x86_pv_vcpu_xsave : "x86 PV vcpu xsave",
REC_TYPE_shared_info : "Shared info",
REC_TYPE_tsc_info : "TSC info",
REC_TYPE_hvm_context : "HVM context",
REC_TYPE_hvm_params : "HVM params",
REC_TYPE_toolstack : "Toolstack",
REC_TYPE_x86_pv_vcpu_msrs : "x86 PV vcpu msrs",
REC_TYPE_verify : "Verify",
REC_TYPE_checkpoint : "Checkpoint",
REC_TYPE_checkpoint_dirty_pfn_list : "Checkpoint dirty pfn list",
REC_TYPE_static_data_end : "Static data end",
REC_TYPE_x86_cpuid_policy : "x86 CPUID policy",
REC_TYPE_x86_msr_policy : "x86 MSR policy",
}
# page_data
PAGE_DATA_FORMAT = "II"
PAGE_DATA_PFN_MASK = (1 << 52) - 1
PAGE_DATA_PFN_RESZ_MASK = ((1 << 60) - 1) & ~((1 << 52) - 1)
# flags from xen/public/domctl.h: XEN_DOMCTL_PFINFO_* shifted by 32 bits
PAGE_DATA_TYPE_SHIFT = 60
PAGE_DATA_TYPE_LTABTYPE_MASK = (0x7 << PAGE_DATA_TYPE_SHIFT)
PAGE_DATA_TYPE_LTAB_MASK = (0xf << PAGE_DATA_TYPE_SHIFT)
PAGE_DATA_TYPE_LPINTAB = (0x8 << PAGE_DATA_TYPE_SHIFT) # Pinned pagetable
PAGE_DATA_TYPE_NOTAB = (0x0 << PAGE_DATA_TYPE_SHIFT) # Regular page
PAGE_DATA_TYPE_L1TAB = (0x1 << PAGE_DATA_TYPE_SHIFT) # L1 pagetable
PAGE_DATA_TYPE_L2TAB = (0x2 << PAGE_DATA_TYPE_SHIFT) # L2 pagetable
PAGE_DATA_TYPE_L3TAB = (0x3 << PAGE_DATA_TYPE_SHIFT) # L3 pagetable
PAGE_DATA_TYPE_L4TAB = (0x4 << PAGE_DATA_TYPE_SHIFT) # L4 pagetable
PAGE_DATA_TYPE_BROKEN = (0xd << PAGE_DATA_TYPE_SHIFT) # Broken
PAGE_DATA_TYPE_XALLOC = (0xe << PAGE_DATA_TYPE_SHIFT) # Allocate-only
PAGE_DATA_TYPE_XTAB = (0xf << PAGE_DATA_TYPE_SHIFT) # Invalid
# x86_pv_info
X86_PV_INFO_FORMAT = "BBHI"
X86_PV_P2M_FRAMES_FORMAT = "II"
# x86_pv_vcpu_{basic,extended,xsave,msrs}
X86_PV_VCPU_HDR_FORMAT = "II"
# x86_tsc_info
X86_TSC_INFO_FORMAT = "IIQII"
# hvm_params
HVM_PARAMS_ENTRY_FORMAT = "QQ"
HVM_PARAMS_FORMAT = "II"
# x86_cpuid_policy => xen_cpuid_leaf_t[]
X86_CPUID_POLICY_FORMAT = "IIIIII"
# x86_msr_policy => xen_msr_entry_t[]
X86_MSR_POLICY_FORMAT = "QII"
class VerifyLibxc(VerifyBase):
""" Verify a Libxc v2 (or later) stream """
def __init__(self, info, read):
VerifyBase.__init__(self, info, read)
self.version = 0
self.squashed_pagedata_records = 0
def verify(self):
""" Verity a libxc stream """
self.verify_ihdr()
self.verify_dhdr()
while self.verify_record() != REC_TYPE_end:
pass
def verify_ihdr(self):
""" Verify an Image Header """
marker, ident, version, options, res1, res2 = \
self.unpack_exact(IHDR_FORMAT)
if marker != IHDR_MARKER:
raise StreamError("Bad image marker: Expected 0x%x, got 0x%x" %
(IHDR_MARKER, marker))
if ident != IHDR_IDENT:
raise StreamError("Bad image id: Expected 0x%x, got 0x%x" %
(IHDR_IDENT, ident))
if not (2 <= version <= 3):
raise StreamError(
"Unknown image version: Expected 2 <= ver <= 3, got %d" %
(version, ))
self.version = version
if options & IHDR_OPT_RESZ_MASK:
raise StreamError("Reserved bits set in image options field: 0x%x" %
(options & IHDR_OPT_RESZ_MASK))
if res1 != 0 or res2 != 0:
raise StreamError(
"Reserved bits set in image header: 0x%04x:0x%08x" %
(res1, res2))
if ( (sys.byteorder == "little") and
((options & IHDR_OPT_BIT_ENDIAN) != IHDR_OPT_LE) ):
raise StreamError(
"Stream is not native endianess - unable to validate")
endian = ["little", "big"][options & IHDR_OPT_LE]
self.info("Libxc Image Header: Version %d, %s endian" %
(version, endian))
def verify_dhdr(self):
""" Verify a domain header """
gtype, page_shift, res1, major, minor = \
self.unpack_exact(DHDR_FORMAT)
if gtype not in dhdr_type_to_str:
raise StreamError("Unrecognised domain type 0x%x" % (gtype, ))
if res1 != 0:
raise StreamError("Reserved bits set in domain header 0x%04x" %
(res1, ))
if page_shift != 12:
raise StreamError("Page shift expected to be 12. Got %d" %
(page_shift, ))
if major == 0:
self.info("Domain Header: legacy converted %s" %
(dhdr_type_to_str[gtype], ))
else:
self.info("Domain Header: %s from Xen %d.%d" %
(dhdr_type_to_str[gtype], major, minor))
def verify_record(self):
""" Verify an individual record """
rtype, length = self.unpack_exact(RH_FORMAT)
if rtype not in rec_type_to_str:
raise StreamError("Unrecognised record type 0x%x" % (rtype, ))
contentsz = (length + 7) & ~7
content = self.rdexact(contentsz)
if rtype != REC_TYPE_page_data:
if self.squashed_pagedata_records > 0:
self.info("Squashed %d Page Data records together" %
(self.squashed_pagedata_records, ))
self.squashed_pagedata_records = 0
self.info("Libxc Record: %s, length %d" %
(rec_type_to_str[rtype], length))
else:
self.squashed_pagedata_records += 1
padding = content[length:]
if padding != b"\x00" * len(padding):
raise StreamError("Padding containing non0 bytes found")
if rtype not in record_verifiers:
raise RuntimeError(
"No verification function for libxc record '%s'" %
rec_type_to_str[rtype])
else:
record_verifiers[rtype](self, content[:length])
return rtype
def verify_record_end(self, content):
""" End record """
if len(content) != 0:
raise RecordError("End record with non-zero length")
def verify_record_page_data(self, content):
""" Page Data record """
minsz = calcsize(PAGE_DATA_FORMAT)
if len(content) <= minsz:
raise RecordError(
"PAGE_DATA record must be at least %d bytes long" % (minsz, ))
count, res1 = unpack(PAGE_DATA_FORMAT, content[:minsz])
if res1 != 0:
raise StreamError(
"Reserved bits set in PAGE_DATA record 0x%04x" % (res1, ))
pfnsz = count * 8
if (len(content) - minsz) < pfnsz:
raise RecordError(
"PAGE_DATA record must contain a pfn record for each count")
pfns = list(unpack("=%dQ" % (count, ), content[minsz:minsz + pfnsz]))
nr_pages = 0
for idx, pfn in enumerate(pfns):
if pfn & PAGE_DATA_PFN_RESZ_MASK:
raise RecordError("Reserved bits set in pfn[%d]: 0x%016x" %
(idx, pfn & PAGE_DATA_PFN_RESZ_MASK))
if pfn >> PAGE_DATA_TYPE_SHIFT in (5, 6, 7, 8):
raise RecordError("Invalid type value in pfn[%d]: 0x%016x" %
(idx, pfn & PAGE_DATA_TYPE_LTAB_MASK))
# We expect page data for each normal page or pagetable
if PAGE_DATA_TYPE_NOTAB <= (pfn & PAGE_DATA_TYPE_LTABTYPE_MASK) \
<= PAGE_DATA_TYPE_L4TAB:
nr_pages += 1
pagesz = nr_pages * 4096
if len(content) != minsz + pfnsz + pagesz:
raise RecordError("Expected %u + %u + %u, got %u" %
(minsz, pfnsz, pagesz, len(content)))
def verify_record_x86_pv_info(self, content):
""" x86 PV Info record """
expectedsz = calcsize(X86_PV_INFO_FORMAT)
if len(content) != expectedsz:
raise RecordError("x86_pv_info: expected length of %d, got %d" %
(expectedsz, len(content)))
width, levels, res1, res2 = unpack(X86_PV_INFO_FORMAT, content)
if width not in (4, 8):
raise RecordError("Expected width of 4 or 8, got %d" % (width, ))
if levels not in (3, 4):
raise RecordError("Expected levels of 3 or 4, got %d" % (levels, ))
if res1 != 0 or res2 != 0:
raise StreamError(
"Reserved bits set in X86_PV_INFO: 0x%04x 0x%08x" %
(res1, res2))
bitness = {4:32, 8:64}[width]
self.info(" %sbit guest, %d levels of pagetables" % (bitness, levels))
def verify_record_x86_pv_p2m_frames(self, content):
""" x86 PV p2m frames record """
if len(content) < 8:
raise RecordError("x86_pv_p2m_frames: record length must be at"
" least 8 bytes long")
if len(content) % 8 != 0:
raise RecordError("Length expected to be a multiple of 8, not %d" %
(len(content), ))
start, end = unpack("=II", content[:8])
self.info(" Start pfn 0x%x, End 0x%x" % (start, end))
def verify_record_x86_pv_vcpu_generic(self, content, name):
""" Generic for all REC_TYPE_x86_pv_vcpu_{basic,extended,xsave,msrs} """
minsz = calcsize(X86_PV_VCPU_HDR_FORMAT)
if len(content) < minsz:
raise RecordError(
"X86_PV_VCPU_%s record length must be at least %d bytes long" %
(name, minsz))
if len(content) == minsz:
self.info("Warning: X86_PV_VCPU_%s record with zero content" %
(name, ))
vcpuid, res1 = unpack(X86_PV_VCPU_HDR_FORMAT, content[:minsz])
if res1 != 0:
raise StreamError(
"Reserved bits set in x86_pv_vcpu_%s record 0x%04x" %
(name, res1))
self.info(" vcpu%d %s context, %d bytes" %
(vcpuid, name, len(content) - minsz))
def verify_record_shared_info(self, content):
""" shared info record """
contentsz = len(content)
if contentsz != 4096:
raise RecordError("Length expected to be 4906 bytes, not %d" %
(contentsz, ))
def verify_record_tsc_info(self, content):
""" tsc info record """
sz = calcsize(X86_TSC_INFO_FORMAT)
if len(content) != sz:
raise RecordError("Length should be %u bytes" % (sz, ))
mode, khz, nsec, incarn, res1 = unpack(X86_TSC_INFO_FORMAT, content)
if res1 != 0:
raise StreamError("Reserved bits set in X86_TSC_INFO: 0x%08x" %
(res1, ))
self.info(" Mode %u, %u kHz, %u ns, incarnation %d" %
(mode, khz, nsec, incarn))
def verify_record_hvm_context(self, content):
""" hvm context record """
if len(content) == 0:
raise RecordError("Zero length HVM context")
def verify_record_hvm_params(self, content):
""" hvm params record """
sz = calcsize(HVM_PARAMS_FORMAT)
if len(content) < sz:
raise RecordError("Length should be at least %u bytes" % (sz, ))
count, rsvd = unpack(HVM_PARAMS_FORMAT, content[:sz])
if rsvd != 0:
raise RecordError("Reserved field not zero (0x%04x)" % (rsvd, ))
if count == 0:
self.info("Warning: HVM_PARAMS record with zero content")
sz += count * calcsize(HVM_PARAMS_ENTRY_FORMAT)
if len(content) != sz:
raise RecordError("Length should be %u bytes" % (sz, ))
def verify_record_toolstack(self, _):
""" toolstack record """
raise DeprecationWarning("Found Toolstack record in stream")
def verify_record_verify(self, content):
""" verify record """
if len(content) != 0:
raise RecordError("Verify record with non-zero length")
def verify_record_checkpoint(self, content):
""" checkpoint record """
if len(content) != 0:
raise RecordError("Checkpoint record with non-zero length")
def verify_record_checkpoint_dirty_pfn_list(self, content):
""" checkpoint dirty pfn list """
raise RecordError("Found checkpoint dirty pfn list record in stream")
def verify_record_static_data_end(self, content):
""" static data end record """
if len(content) != 0:
raise RecordError("End record with non-zero length")
if self.version < 3:
raise RecordError("Static data end record found in v2 stream")
def verify_record_x86_cpuid_policy(self, content):
""" x86 CPUID policy record """
if self.version < 3:
raise RecordError("x86 CPUID policy record found in v2 stream")
sz = calcsize(X86_CPUID_POLICY_FORMAT)
contentsz = len(content)
if contentsz < sz or (contentsz % sz) != 0:
raise RecordError("Record length %u, expected multiple of %u" %
(contentsz, sz))
def verify_record_x86_msr_policy(self, content):
""" x86 MSR policy record """
if self.version < 3:
raise RecordError("x86 MSR policy record found in v2 stream")
sz = calcsize(X86_MSR_POLICY_FORMAT)
contentsz = len(content)
if contentsz < sz or (contentsz % sz) != 0:
raise RecordError("Record length %u, expected multiple of %u" %
(contentsz, sz))
record_verifiers = {
REC_TYPE_end:
VerifyLibxc.verify_record_end,
REC_TYPE_page_data:
VerifyLibxc.verify_record_page_data,
REC_TYPE_x86_pv_info:
VerifyLibxc.verify_record_x86_pv_info,
REC_TYPE_x86_pv_p2m_frames:
VerifyLibxc.verify_record_x86_pv_p2m_frames,
REC_TYPE_x86_pv_vcpu_basic:
lambda s, x:
VerifyLibxc.verify_record_x86_pv_vcpu_generic(s, x, "basic"),
REC_TYPE_x86_pv_vcpu_extended:
lambda s, x:
VerifyLibxc.verify_record_x86_pv_vcpu_generic(s, x, "extended"),
REC_TYPE_x86_pv_vcpu_xsave:
lambda s, x:
VerifyLibxc.verify_record_x86_pv_vcpu_generic(s, x, "xsave"),
REC_TYPE_x86_pv_vcpu_msrs:
lambda s, x:
VerifyLibxc.verify_record_x86_pv_vcpu_generic(s, x, "msrs"),
REC_TYPE_shared_info:
VerifyLibxc.verify_record_shared_info,
REC_TYPE_tsc_info:
VerifyLibxc.verify_record_tsc_info,
REC_TYPE_hvm_context:
VerifyLibxc.verify_record_hvm_context,
REC_TYPE_hvm_params:
VerifyLibxc.verify_record_hvm_params,
REC_TYPE_toolstack:
VerifyLibxc.verify_record_toolstack,
REC_TYPE_verify:
VerifyLibxc.verify_record_verify,
REC_TYPE_checkpoint:
VerifyLibxc.verify_record_checkpoint,
REC_TYPE_checkpoint_dirty_pfn_list:
VerifyLibxc.verify_record_checkpoint_dirty_pfn_list,
REC_TYPE_static_data_end:
VerifyLibxc.verify_record_static_data_end,
REC_TYPE_x86_cpuid_policy:
VerifyLibxc.verify_record_x86_cpuid_policy,
REC_TYPE_x86_msr_policy:
VerifyLibxc.verify_record_x86_msr_policy,
}
|