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