|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v5 RFC 02/14] scripts: Scripts for inspection/valdiation of legacy and new streams
TODO: move to tools/python and install properly...
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Signed-off-by: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx>
Signed-off-by: David Vrabel <david.vrabel@xxxxxxxxxx>
---
tools/libxc/saverestore/scripts/legacy.py | 576 +++++++++++++++++++++++++
tools/libxc/saverestore/scripts/streamspec.py | 136 ++++++
tools/libxc/saverestore/scripts/verify.py | 377 ++++++++++++++++
3 files changed, 1089 insertions(+)
create mode 100755 tools/libxc/saverestore/scripts/legacy.py
create mode 100644 tools/libxc/saverestore/scripts/streamspec.py
create mode 100755 tools/libxc/saverestore/scripts/verify.py
diff --git a/tools/libxc/saverestore/scripts/legacy.py
b/tools/libxc/saverestore/scripts/legacy.py
new file mode 100755
index 0000000..a1b0070
--- /dev/null
+++ b/tools/libxc/saverestore/scripts/legacy.py
@@ -0,0 +1,576 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import struct
+import streamspec
+import os
+
+__version__ = 1
+
+fin = None # Input file/fd
+fout = None # Output file/fd
+twidth = 0 # Legacy toolstack bitness (32 or 64)
+pv = None # Boolean (pv or hvm)
+
+def stream_read(_ = None):
+ return fin.read(_)
+
+def stream_write(_):
+ return fout.write(_)
+
+class StreamError(StandardError):
+ pass
+
+class VM(object):
+
+ def __init__(self):
+ # Common
+ self.p2m_size = 0
+
+ # PV
+ self.max_vcpu_id = 0
+ self.online_vcpu_map = []
+ self.width = 0
+ self.levels = 0
+ self.basic_len = 0
+ self.extd = False
+ self.xsave_len = 0
+
+def write_ihdr():
+ stream_write(struct.pack(streamspec.IHDR_FORMAT,
+ streamspec.IHDR_MARKER, # Marker
+ streamspec.IHDR_IDENT, # Ident
+ 1, # Version
+ streamspec.IHDR_OPT_LE, # Options
+ 0, 0)) # Reserved
+
+def write_dhdr():
+ if pv:
+ dtype = streamspec.DHDR_TYPE_x86_pv
+ else:
+ dtype = streamspec.DHDR_TYPE_x86_hvm
+
+ stream_write(struct.pack(streamspec.DHDR_FORMAT,
+ dtype, # Type
+ 12, # Page size
+ 0, # Reserved
+ 0, # Xen major (converted)
+ __version__)) # Xen minor (converted)
+
+def write_record(rt, *argl):
+ alldata = ''.join(argl)
+ length = len(alldata)
+
+ record = struct.pack(streamspec.RH_FORMAT, rt, length) + alldata
+ plen = (8 - (length & 7)) & 7
+ record += '\x00' * plen
+
+ stream_write(record)
+
+def write_pv_info(vm):
+ write_record(streamspec.REC_TYPE_x86_pv_info,
+ struct.pack(streamspec.X86_PV_INFO_FORMAT,
+ vm.width, vm.levels, 0, 0))
+
+def write_pv_p2m_frames(vm, pfns):
+ write_record(streamspec.REC_TYPE_x86_pv_p2m_frames,
+ struct.pack(streamspec.X86_PV_P2M_FRAMES_FORMAT,
+ 0, vm.p2m_size - 1),
+ struct.pack("Q" * len(pfns), *pfns))
+
+def write_pv_vcpu_basic(vcpu_id, data):
+ write_record(streamspec.REC_TYPE_x86_pv_vcpu_basic,
+ struct.pack(streamspec.X86_PV_VCPU_FORMAT_HDR, vcpu_id, 0),
+ data)
+
+def write_pv_vcpu_extd(vcpu_id, data):
+ write_record(streamspec.REC_TYPE_x86_pv_vcpu_extended,
+ struct.pack(streamspec.X86_PV_VCPU_FORMAT_HDR, vcpu_id, 0),
+ data)
+
+def write_pv_vcpu_xsave(vcpu_id, data):
+ write_record(streamspec.REC_TYPE_x86_pv_vcpu_xsave,
+ struct.pack(streamspec.X86_PV_VCPU_FORMAT_HDR, vcpu_id, 0),
+ data)
+
+def write_page_data(pfns, pages):
+ if fout is None: # Safe copying 1M buffers around for no reason
+ return
+
+ new_pfns = [(((x & 0xf0000000) << 32) | (x & 0x0fffffff)) for x in pfns]
+
+ # Optimise the needless buffer copying in write_record()
+ stream_write(struct.pack(streamspec.RH_FORMAT,
+ streamspec.REC_TYPE_page_data,
+ 8 + (len(new_pfns) * 8) + len(pages)))
+ stream_write(struct.pack(streamspec.PAGE_DATA_FORMAT, len(new_pfns), 0))
+ stream_write(struct.pack("Q" * len(new_pfns), *new_pfns))
+ stream_write(pages)
+
+def write_tsc_info(mode, khz, nsec, incarn):
+ write_record(streamspec.REC_TYPE_tsc_info,
+ struct.pack(streamspec.TSC_INFO_FORMAT,
+ mode, khz, nsec, incarn, 0))
+
+def write_hvm_params(params):
+ if pv:
+ raise StreamError("HVM-only param in PV stream")
+ elif len(params) % 2:
+ raise RuntimeError("Expected even length list of hvm parameters")
+
+ write_record(streamspec.REC_TYPE_hvm_params,
+ struct.pack(streamspec.HVM_PARAMS_FORMAT, len(params) / 2, 0),
+ struct.pack("Q" * len(params), *params))
+
+
+def rdexact(nr_bytes):
+ """Read exactly nr_bytes from fin"""
+ _ = stream_read(nr_bytes)
+ if len(_) != nr_bytes:
+ raise IOError("Stream truncated")
+ return _
+
+def unpack_exact(fmt):
+ """Unpack a format from fin"""
+ sz = struct.calcsize(fmt)
+ return struct.unpack(fmt, rdexact(sz))
+
+def unpack_ulongs(nr_ulongs):
+ if twidth == 32:
+ return unpack_exact("I" * nr_ulongs)
+ else:
+ return unpack_exact("Q" * nr_ulongs)
+
+def skip_xl_header():
+
+ hdr = rdexact(32)
+ if hdr != "Xen saved domain, xl format\n \0 \r":
+ raise StreamError("No xl header")
+
+ opts = rdexact(16)
+ _, _, _, optlen = struct.unpack("=IIII", opts)
+
+ optdata = rdexact(optlen)
+
+ print "Skipped xl header"
+
+ stream_write(hdr)
+ stream_write(opts)
+ stream_write(optdata)
+
+def read_pv_extended_info(vm):
+
+ marker, = unpack_ulongs(1)
+
+ if twidth == 32:
+ expected = 0xffffffff
+ else:
+ expected = 0xffffffffffffffff
+
+ if marker != expected:
+ raise StreamError("Unexpected extended info marker 0x%x" % (marker, ))
+
+ total_length, = unpack_exact("I")
+ so_far = 0
+
+ print "Extended Info: length 0x%x" % (total_length, )
+
+ while so_far < total_length:
+
+ blkid, datasz = unpack_exact("=4sI")
+ so_far += 8
+
+ print " Record type: %s, size 0x%x" % (blkid, datasz)
+
+ data = rdexact(datasz)
+ so_far += datasz
+
+ # Eww, but this is how it is done :(
+ if blkid == "vcpu":
+
+ vm.basic_len = datasz
+
+ if datasz == 0x1430:
+ vm.width = 8
+ vm.levels = 4
+ print " 64bit domain, 4 levels"
+ elif datasz == 0xaf0:
+ vm.width = 4
+ vm.levels = 3
+ print " 32bit domain, 3 levels"
+ else:
+ raise StreamError("Unable to determine guest width/level")
+
+ write_pv_info(vm)
+
+ elif blkid == "extv":
+ vm.extd = True
+
+ elif blkid == "xcnt":
+ vm.xsave_len, = struct.unpack_from("I", data, 0)
+ print "xcnt sz 0x%x" % (vm.xsave_len, )
+
+ else:
+ raise StreamError("Unrecognised extended block")
+
+
+ if so_far != total_length:
+ raise StreamError("Overshot Extended Info size by %d bytes"
+ % (so_far - total_length,))
+
+def read_pv_p2m_frames(vm):
+ fpp = 4096 / vm.width
+ p2m_frame_len = (vm.p2m_size - 1) / fpp + 1
+
+ print "P2M frames: fpp %d, p2m_frame_len %d" % (fpp, p2m_frame_len)
+ write_pv_p2m_frames(vm, unpack_ulongs(p2m_frame_len))
+
+def read_pv_tail(vm):
+
+ nr_unmapped_pfns, = unpack_exact("I")
+
+ if nr_unmapped_pfns != 0:
+ # "Unmapped" pfns are bogus
+ _ = unpack_ulongs(nr_unmapped_pfns)
+ print "discarding %d bogus 'unmapped pfns'" % (nr_unmapped_pfns, )
+ #raise StreamError("Found bogus 'unmapped pfns'")
+
+ for vcpu_id in vm.online_vcpu_map:
+
+ basic = rdexact(vm.basic_len)
+ print "Got VCPU basic (size 0x%x)" % (vm.basic_len, ),
+ write_pv_vcpu_basic(vcpu_id, basic)
+
+ if vm.extd:
+ extd = rdexact(128)
+ print "extd (size 0x%x)" % (128, ),
+ write_pv_vcpu_extd(vcpu_id, extd)
+
+ if vm.xsave_len:
+ mask, size = unpack_exact("QQ")
+ assert vm.xsave_len - 16 == size
+
+ xsave = rdexact(size)
+ print "xsave (mask 0x%x, size 0x%x)" % (mask, size),
+ write_pv_vcpu_xsave(vcpu_id, xsave)
+ print ""
+
+ shinfo = rdexact(4096)
+ print "Got shinfo"
+
+ write_record(streamspec.REC_TYPE_shared_info, shinfo)
+ write_record(streamspec.REC_TYPE_end, "")
+
+
+def read_chunks(vm):
+
+ hvm_params = []
+
+ while True:
+
+ marker, = unpack_exact("=i")
+ if marker <= 0:
+ print "Chunk: type 0x%x" % (marker, )
+
+ if marker == 0:
+ print " End"
+
+ if hvm_params:
+ write_hvm_params(hvm_params)
+
+ return
+
+ elif marker > 0:
+
+ if marker > 1024:
+ raise StreamError("Page batch (%d) exceeded MAX_BATCH"
+ % (marker, ))
+ pfns = unpack_ulongs(marker)
+
+ # xc_domain_save() leaves many XEN_DOMCTL_PFINFO_XTAB records for
+ # sequences of pfns it cant map. Drop these.
+ pfns = [ x for x in pfns if x != 0xf0000000 ]
+
+ if len(set(pfns)) != len(pfns):
+ raise StreamError("Duplicate pfns in batch")
+
+ # print "0x[",
+ # for pfn in pfns:
+ # print "%x" % (pfn, ),
+ # print "]"
+
+ nr_pages = len([x for x in pfns if (x & 0xf0000000) < 0xd0000000])
+
+ #print " Page Batch, %d PFNs, %d pages" % (marker, nr_pages)
+ pages = rdexact(nr_pages * 4096)
+
+ write_page_data(pfns, pages)
+
+ elif marker == -1: # XC_SAVE_ID_ENABLE_VERIFY_MODE
+ # Verify mode... Seemingly nothing to do...
+ pass
+
+ elif marker == -2: # XC_SAVE_ID_VCPU_INFO
+ max_id, = unpack_exact("i")
+
+ if max_id > 4095:
+ raise StreamError("Vcpu max_id out of range: %d > 4095"
+ % (max_id, ) )
+
+ vm.max_vcpu_id = max_id
+ bitmap = unpack_exact("Q" * ((max_id/64) + 1))
+
+ for idx, word in enumerate(bitmap):
+ bit_idx = 0
+
+ while word > 0:
+ if word & 1:
+ vm.online_vcpu_map.append((idx * 64) + bit_idx)
+
+ bit_idx += 1
+ word >>= 1
+
+ print " Vcpu info: max_id %d, online map %s" % (vm.max_vcpu_id,
+
vm.online_vcpu_map)
+
+ elif marker == -3: # XC_SAVE_ID_HVM_IDENT_PT
+ _, ident_pt = unpack_exact("=IQ")
+ print " EPT Identity Pagetable 0x%x" % (ident_pt, )
+ hvm_params.extend([12, # HVM_PARAM_IDENT_PT
+ ident_pt])
+
+ elif marker == -4: # XC_SAVE_ID_HVM_VM86_TSS
+ _, vm86_tss = unpack_exact("=IQ")
+ print " VM86 TSS: 0x%x" % (vm86_tss, )
+ hvm_params.extend([15, # HVM_PARAM_VM86_TSS
+ vm86_tss])
+
+ elif marker == -5: # XC_SAVE_ID_TMEM
+ raise RuntimeError("todo")
+
+ elif marker == -6: # XC_SAVE_ID_TMEM_EXTRA
+ raise RuntimeError("todo")
+
+ elif marker == -7: # XC_SAVE_ID_TSC_INFO
+ mode, nsec, khz, incarn = unpack_exact("=IQII")
+ print (" TSC_INFO: mode %s, %d ns, %d khz, %d incarn"
+ % (mode, nsec, khz, incarn))
+ write_tsc_info(mode, khz, nsec, incarn)
+
+ elif marker == -8: # XC_SAVE_ID_HVM_CONSOLE_PFN
+ _, console_pfn = unpack_exact("=IQ")
+ print " Console pfn 0x%x" % (console_pfn, )
+ hvm_params.extend([17, # HVM_PARAM_CONSOLE_PFN
+ console_pfn])
+
+ elif marker == -9: # XC_SAVE_ID_LAST_CHECKPOINT
+ print " Last Checkpoint"
+ # Nothing to do
+
+ elif marker == -10: # XC_SAVE_ID_HVM_ACPI_IOPORTS_LOCATION
+ _, loc = unpack_exact("=IQ")
+ print " ACPI ioport location 0x%x" % (loc, )
+ hvm_params.extend([19, # HVM_PARAM_ACPI_IOPORTS_LOCATION
+ loc])
+
+ elif marker == -11: # XC_SAVE_ID_HVM_VIRIDIAN
+ _, loc = unpack_exact("=IQ")
+ print " Viridian location 0x%x" % (loc, )
+ hvm_params.extend([9, # HVM_PARAM_VIRIDIAN
+ loc])
+
+ elif marker == -12: # XC_SAVE_ID_COMPRESSED_DATA
+ sz, = unpack_exact("I")
+ data = rdexact(sz)
+ print " Compressed Data: sz 0x%x" % (sz, )
+ raise RuntimeError("todo")
+
+ elif marker == -13: # XC_SAVE_ID_ENABLE_COMPRESSION
+ raise RuntimeError("todo")
+
+ elif marker == -14: # XC_SAVE_ID_HVM_GENERATION_ID_ADDR
+ _, genid_loc = unpack_exact("=IQ")
+ print " Generation ID Address 0x%x" % (genid_loc, )
+ hvm_params.extend([32, # HVM_PARAM_VM_GENERATION_ID_ADDR
+ genid_loc])
+
+ elif marker == -15: # XC_SAVE_ID_HVM_PAGING_RING_PFN
+ _, paging_ring_pfn = unpack_exact("=IQ")
+ print " Paging ring pfn 0x%x" % (paging_ring_pfn, )
+ hvm_params.extend([27, # HVM_PARAM_PAGING_RING_PFN
+ paging_ring_pfn])
+
+ elif marker == -16: # XC_SAVE_ID_HVM_ACCESS_RING_PFN
+ _, access_ring_pfn = unpack_exact("=IQ")
+ print " Access ring pfn 0x%x" % (access_ring_pfn, )
+ hvm_params.extend([28, # HVM_PARAM_ACCESS_RING_PFN
+ access_ring_pfn])
+
+ elif marker == -17: # XC_SAVE_ID_HVM_SHARING_RING_PFN
+ _, sharing_ring_pfn = unpack_exact("=IQ")
+ print " Sharing ring pfn 0x%x" % (sharing_ring_pfn, )
+ hvm_params.extend([29, # HVM_PARAM_SHARING_RING_PFN
+ sharing_ring_pfn])
+
+ elif marker == -18:
+ sz, = unpack_exact("I")
+ data = rdexact(sz)
+ print " Toolstack Data: sz 0x%x" % (sz, )
+ print >> sys.stderr, "TODO - fix libxl's use of this"
+ write_record(streamspec.REC_TYPE_toolstack, data)
+
+ else:
+ raise StreamError("Unrecognised chunk")
+
+def read_hvm_tail(vm):
+
+ io, bufio, store = unpack_exact("QQQ")
+ print "Magic pfns: 0x%x 0x%x 0x%x" % (io, bufio, store)
+ write_hvm_params([5, io, # HVM_PARAM_IOREQ_PFN
+ 6, bufio, # HVM_PARAM_BUFIOREQ_PFN
+ 1, store]) # HVM_PARAM_STORE_PFN
+
+ blobsz, = unpack_exact("I")
+ print "Got HVM Context (0x%x bytes)" % (blobsz, )
+ blob = rdexact(blobsz)
+
+ write_record(streamspec.REC_TYPE_hvm_context, blob)
+ write_record(streamspec.REC_TYPE_end, "")
+
+
+
+def read_qemu(vm):
+
+ rawsig = rdexact(21)
+ sig, = struct.unpack("21s", rawsig)
+ print "Qemu signature: %s" % (sig, )
+
+ if sig == "DeviceModelRecord0002":
+ rawsz = rdexact(4)
+ sz, = struct.unpack("I", rawsz)
+ qdata = rdexact(sz)
+
+ stream_write(rawsig)
+ stream_write(rawsz)
+ stream_write(qdata)
+
+ else:
+ raise RuntimeError("Unrecognised Qemu sig '%s'" % (sig, ))
+
+
+def read_vm(vm):
+
+ try:
+
+ vm.p2m_size, = unpack_ulongs(1)
+ print "P2M Size: 0x%x" % (vm.p2m_size,)
+
+ write_ihdr()
+ write_dhdr()
+
+ if pv:
+ read_pv_extended_info(vm)
+ read_pv_p2m_frames(vm)
+
+ read_chunks(vm)
+
+ if pv:
+ read_pv_tail(vm)
+ else:
+ read_hvm_tail(vm)
+ read_qemu(vm)
+
+ except (IOError, StreamError, ) as e:
+ print >> sys.stderr, "Error: ", e
+ return 1
+
+ except RuntimeError as e:
+ print >> sys.stderr, "Script error", e
+ print >> sys.stderr, "Please fix me"
+ return 2
+ return 0
+
+def open_file_or_fd(val, mode):
+ """
+ If 'val' looks like a decimal integer, open it as an fd. If not, try to
+ open it as a regular file.
+ """
+
+ fd = -1
+ try:
+ # Does it look like an integer?
+ try:
+ fd = int(val, 10)
+ except ValueError:
+ pass
+
+ # Try to open it...
+ if fd != -1:
+ return os.fdopen(fd, mode)
+ else:
+ return open(val, mode)
+
+ except StandardError, e:
+ if fd != -1:
+ print >> sys.stderr, "Unable to open fd %d: %s" % (fd, e)
+ else:
+ print >> sys.stderr, "Unable to open file '%s': %s" % (val, e)
+
+ raise SystemExit(1)
+
+
+def main(argv):
+ from optparse import OptionParser
+ global fin, fout, twidth, pv
+
+ # Change stdout to be line-buffered.
+ sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
+
+ parser = OptionParser(version = __version__,
+ usage = ("%prog -i INPUT -o OUTPUT"
+ " -w WIDTH -g GUEST [-x]"))
+
+ parser.add_option("-v", action = "store_true", default = False,
+ help = "More verbose")
+
+ # Required options
+ parser.add_option("-i", "--in", dest = "fin", metavar = "<FD or FILE>",
+ help = "Legacy input to convert")
+ parser.add_option("-o", "--out", dest = "fout", metavar = "<FD or FILE>",
+ help = "v2 format output")
+ parser.add_option("-w", "--width", dest = "twidth",
+ metavar = "<32/64>", choices = ["32", "64"],
+ help = "Legacy toostack bitness")
+ parser.add_option("-g", "--guest-type", dest = "gtype",
+ metavar = "<pv/hvm>", choices = ["pv", "hvm"],
+ help = "Type of guest in stream")
+
+ # Optional options
+ parser.add_option("-x", "--xl", action = "store_true", default = False,
+ help = ("Is an `xl` header present in the stream?"
+ " (default no)"))
+
+ opts, args = parser.parse_args()
+
+ if (opts.fin is None or opts.fout is None or
+ opts.twidth is None or opts.gtype is None):
+
+ parser.print_help(sys.stderr)
+ raise SystemExit(1)
+
+ fin = open_file_or_fd(opts.fin, "rb")
+ fout = open_file_or_fd(opts.fout, "wb")
+ twidth = int(opts.twidth)
+ pv = opts.gtype == "pv"
+
+ if opts.xl:
+ skip_xl_header()
+
+ rc = read_vm(VM())
+ fout.close()
+
+ return rc
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/tools/libxc/saverestore/scripts/streamspec.py
b/tools/libxc/saverestore/scripts/streamspec.py
new file mode 100644
index 0000000..9f97b46
--- /dev/null
+++ b/tools/libxc/saverestore/scripts/streamspec.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Python structures for the Migration v2 stream format.
+# See docs/specs/libxc-migration-stream.pandoc
+
+# Image Header
+IHDR_FORMAT = "!QIIHHI"
+
+IHDR_MARKER = 0xffffffffffffffff
+IHDR_IDENT = 0x58454E46 # "XENF" in ASCII
+
+IHDR_OPT_ENDIAN_ = 0
+IHDR_OPT_LE = (0 << IHDR_OPT_ENDIAN_)
+IHDR_OPT_BE = (1 << IHDR_OPT_ENDIAN_)
+
+IHDR_OPT_RESZ_MASK = 0xfffe
+
+# Domain Header
+DHDR_FORMAT = "IHHII"
+
+DHDR_TYPE_x86_pv = 0x00000001
+DHDR_TYPE_x86_hvm = 0x00000002
+DHDR_TYPE_x86_pvh = 0x00000003
+DHDR_TYPE_arm = 0x00000004
+
+dhdr_type_to_str = {
+ DHDR_TYPE_x86_pv : "x86 PV",
+ DHDR_TYPE_x86_hvm : "x86 HVM",
+ DHDR_TYPE_x86_pvh : "x86 PVH",
+ DHDR_TYPE_arm : "ARM",
+}
+
+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_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",
+}
+
+# page_data
+PAGE_DATA_FORMAT = "II"
+PAGE_DATA_PFN_MASK = (1L << 52) - 1
+PAGE_DATA_PFN_RESZ_MASK = ((1L << 60) - 1) & ~((1L << 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 = (0x7L << PAGE_DATA_TYPE_SHIFT)
+PAGE_DATA_TYPE_LTAB_MASK = (0xfL << PAGE_DATA_TYPE_SHIFT)
+PAGE_DATA_TYPE_LPINTAB = (0x8L << PAGE_DATA_TYPE_SHIFT) # Pinned
pagetable
+
+PAGE_DATA_TYPE_NOTAB = (0x0L << PAGE_DATA_TYPE_SHIFT) # Regular page
+PAGE_DATA_TYPE_L1TAB = (0x1L << PAGE_DATA_TYPE_SHIFT) # L1 pagetable
+PAGE_DATA_TYPE_L2TAB = (0x2L << PAGE_DATA_TYPE_SHIFT) # L2 pagetable
+PAGE_DATA_TYPE_L3TAB = (0x3L << PAGE_DATA_TYPE_SHIFT) # L3 pagetable
+PAGE_DATA_TYPE_L4TAB = (0x4L << PAGE_DATA_TYPE_SHIFT) # L4 pagetable
+PAGE_DATA_TYPE_BROKEN = (0xdL << PAGE_DATA_TYPE_SHIFT) # Broken
+PAGE_DATA_TYPE_XALLOC = (0xeL << PAGE_DATA_TYPE_SHIFT) # Allocate-only
+PAGE_DATA_TYPE_XTAB = (0xfL << 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_FORMAT_HDR = "II"
+
+# tsc_info
+TSC_INFO_FORMAT = "IIQII"
+
+# hvm_params
+HVM_PARAMS_ENTRY_FORMAT = "QQ"
+HVM_PARAMS_FORMAT = "II"
+
+#
+# libxl format
+#
+
+LIBXL_QEMU_SIGNATURE = "DeviceModelRecord0002"
+LIBXL_QEMU_RECORD_HDR = "=%dsI" % (len(LIBXL_QEMU_SIGNATURE), )
+
+
+# If run as a python script alone, confirm some expected sizes
+if __name__ == "__main__":
+ import sys
+ from struct import calcsize
+
+ ok = True
+ for fmt, sz in [ ("IHDR_FORMAT", 24),
+ ("DHDR_FORMAT", 16),
+ ("RH_FORMAT", 8),
+
+ ("PAGE_DATA_FORMAT", 8),
+ ("X86_PV_INFO_FORMAT", 8),
+ ("X86_PV_P2M_FRAMES_FORMAT", 8),
+ ("X86_PV_VCPU_FORMAT_HDR", 8),
+ ("TSC_INFO_FORMAT", 24),
+ ("HVM_PARAMS_ENTRY_FORMAT", 16),
+ ("HVM_PARAMS_FORMAT", 8),
+ ]:
+
+ realsz = calcsize(getattr(sys.modules[__name__], fmt))
+ if realsz != sz:
+ print "%s is %d bytes but expected %d" % (fmt, realsz, sz)
+ ok = False
+
+ if ok:
+ sys.exit(0)
+ else:
+ sys.exit(1)
diff --git a/tools/libxc/saverestore/scripts/verify.py
b/tools/libxc/saverestore/scripts/verify.py
new file mode 100755
index 0000000..883cc18
--- /dev/null
+++ b/tools/libxc/saverestore/scripts/verify.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+import struct
+
+from streamspec import *
+
+class StreamError(StandardError):
+ pass
+
+class RecordError(StandardError):
+ pass
+
+def skip_xl_header(stream):
+
+ magic = stream.read(8)
+ if magic != "mat\n \0 \r":
+ return False
+
+ header = stream.read(16)
+ if len(header) != 16:
+ return False
+
+ _, _, _, optlen = struct.unpack("=IIII", header)
+
+ optdata = stream.read(optlen)
+ if len(optdata) != optlen:
+ return False
+
+ return True
+
+
+def verify_ihdr(stream):
+ """ Verify an image header """
+
+ datasz = struct.calcsize(IHDR_FORMAT)
+ data = stream.read(datasz)
+
+ # xl header record?
+ if data == "Xen saved domain, xl for":
+ if skip_xl_header(stream):
+ data = stream.read(datasz)
+ else:
+ raise StreamError("Invalid looking xl header on the stream")
+
+ if len(data) != datasz:
+ raise IOError("Truncated stream")
+
+ marker, id, version, options, res1, res2 = struct.unpack(IHDR_FORMAT, data)
+
+ if marker != 0xffffffffffffffff:
+ raise StreamError("Bad image marker: Expected 0xffffffffffffffff, "
+ "got 0x%x" % (marker, ))
+
+ if id != 0x58454e46:
+ raise StreamError("Bad image id: Expected 0x0x58454e46, got 0x%x"
+ % (id, ))
+
+ if version != 1:
+ raise StreamError("Unknown image version: Expected 1, got %d"
+ % (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_ENDIAN_) != IHDR_OPT_LE ):
+ raise StreamError("Stream is not native endianess - unable to
validate")
+
+ print "Valid Image Header:",
+ if options & IHDR_OPT_BE:
+ print "big endian"
+ else:
+ print "little endian"
+
+def verify_dhdr(stream):
+ """ Verify a domain header """
+
+ datasz = struct.calcsize(DHDR_FORMAT)
+ data = stream.read(datasz)
+
+ if len(data) != datasz:
+ raise IOError("Truncated stream")
+
+ type, page_shift, res1, major, minor = struct.unpack(DHDR_FORMAT, data)
+
+ if type not in dhdr_type_to_str:
+ raise StreamError("Unrecognised domain type 0x%x" % (type, ))
+
+ 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, ))
+
+ print "Valid Domain Header: %s from Xen %d.%d (page sz %d)" \
+ % (dhdr_type_to_str[type], major, minor, 2**page_shift)
+
+
+def verify_record_end(content):
+
+ if len(content) != 0:
+ raise RecordError("End record with non-zero length")
+
+def verify_page_data(content):
+ minsz = struct.calcsize(PAGE_DATA_FORMAT)
+
+ if len(content) <= minsz:
+ raise RecordError("PAGE_DATA record must be at least %d bytes long"
+ % (minsz, ))
+
+ count, res1 = struct.unpack_from(PAGE_DATA_FORMAT, content)
+
+ 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(struct.unpack_from("=%dQ" % (count,), content, minsz))
+
+ 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_vcpu_generic(content, name):
+ # Generic for all REC_TYPE_x86_pv_vcpu_{basic,extended,xsave,msrs}
+ minsz = struct.calcsize(X86_PV_VCPU_FORMAT_HDR)
+
+ if len(content) <= minsz:
+ raise RecordError("X86_PV_VCPU_%s record length must be at least %d"
+ " bytes long" % (name, minsz))
+
+ vcpuid, res1 = struct.unpack_from(X86_PV_VCPU_FORMAT_HDR, content)
+
+ if res1 != 0:
+ raise StreamError("Reserved bits set in x86_pv_vcpu_%s record 0x%04x"
+ % (name, res1))
+
+ print " vcpu%d %s context, %d bytes" % (vcpuid, name, len(content) -
minsz)
+
+
+def verify_x86_pv_info(content):
+
+ expectedsz = struct.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 = struct.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]
+
+ print " %sbit guest, %d levels of pagetables" % (bitness, levels)
+
+def verify_x86_pv_p2m_frames(content):
+
+ if len(content) % 8 != 0:
+ raise RecordError("Length expected to be a multiple of 8, not %d"
+ % (len(content), ))
+
+ start, end = struct.unpack_from("=II", content)
+
+ print " Start pfn 0x%x, End 0x%x" % (start, end)
+
+def verify_record_shared_info(content):
+
+ if len(content) != 4096:
+ raise RecordError("Length expected to be 4906 bytes, not %d"
+ % (len(content), ))
+
+def verify_record_tsc_info(content):
+
+ sz = struct.calcsize(TSC_INFO_FORMAT)
+
+ if len(content) != sz:
+ raise RecordError("Length should be %u bytes" % (sz, ))
+
+ mode, khz, nsec, incarn, res1 = struct.unpack(TSC_INFO_FORMAT, content)
+
+ if res1 != 0:
+ raise StreamError("Reserved bits set in TSC_INFO: 0x%08x" % (res1, ))
+
+ print (" Mode %u, %u kHz, %u ns, incarnation %d"
+ % (mode, khz, nsec, incarn))
+
+def verify_record_hvm_context(content):
+
+ if len(content) == 0:
+ raise RecordError("Zero length HVM context")
+
+def verify_record_hvm_params(content):
+
+ sz = struct.calcsize(HVM_PARAMS_FORMAT)
+
+ if len(content) < sz:
+ raise RecordError("Length should be at least %u bytes" % (sz, ))
+
+ count, rsvd = struct.unpack(HVM_PARAMS_FORMAT, content[:sz])
+
+ if rsvd != 0:
+ raise RecordError("Reserved field not zero (0x%04x)" % (rsvd, ))
+
+ sz += count * struct.calcsize(HVM_PARAMS_ENTRY_FORMAT)
+
+ if len(content) != sz:
+ raise RecordError("Length should be %u bytes" % (sz, ))
+
+def verify_toolstack(content):
+ # Opaque blob -- nothing to verify.
+ pass
+
+record_verifiers = {
+ REC_TYPE_end : verify_record_end,
+ REC_TYPE_page_data : verify_page_data,
+
+ REC_TYPE_x86_pv_info: verify_x86_pv_info,
+ REC_TYPE_x86_pv_p2m_frames: verify_x86_pv_p2m_frames,
+
+ REC_TYPE_x86_pv_vcpu_basic :
+ lambda x: verify_record_x86_pv_vcpu_generic(x, "basic"),
+ REC_TYPE_x86_pv_vcpu_extended :
+ lambda x: verify_record_x86_pv_vcpu_generic(x, "extended"),
+ REC_TYPE_x86_pv_vcpu_xsave :
+ lambda x: verify_record_x86_pv_vcpu_generic(x, "xsave"),
+ REC_TYPE_x86_pv_vcpu_msrs :
+ lambda x: verify_record_x86_pv_vcpu_generic(x, "msrs"),
+
+ REC_TYPE_shared_info: verify_record_shared_info,
+ REC_TYPE_tsc_info: verify_record_tsc_info,
+
+ REC_TYPE_hvm_context: verify_record_hvm_context,
+ REC_TYPE_hvm_params: verify_record_hvm_params,
+ REC_TYPE_toolstack: verify_toolstack,
+}
+
+_squahsed_data_records = 0
+def verify_record(stream):
+ """ Verify a record """
+ global _squahsed_data_records
+
+ datasz = struct.calcsize(RH_FORMAT)
+ data = stream.read(datasz)
+
+ if len(data) != datasz:
+ raise IOError("Truncated stream")
+
+ type, length = struct.unpack(RH_FORMAT, data)
+
+ if type not in rec_type_to_str:
+ raise StreamError("Unrecognised record type %x" % (type, ))
+
+ contentsz = (length + 7) & ~7
+ content = stream.read(contentsz)
+
+ if len(content) != contentsz:
+ raise IOError("Truncated stream")
+
+ padding = content[length:]
+ if padding != "\x00" * len(padding):
+ raise StreamError("Padding containging non0 bytes found")
+
+ if type != REC_TYPE_page_data:
+
+ if _squahsed_data_records > 0:
+ print ("Squashed %d valid Page Data records together"
+ % (_squahsed_data_records, ))
+ _squahsed_data_records = 0
+
+ print ("Valid Record Header: %s, length %d"
+ % (rec_type_to_str[type], length))
+
+ else:
+ _squahsed_data_records += 1
+
+ if type not in record_verifiers:
+ raise RuntimeError("No verification function")
+ else:
+ record_verifiers[type](content[:length])
+
+ return type
+
+def verify_qemu_record(fin):
+
+ sz = struct.calcsize(LIBXL_QEMU_RECORD_HDR)
+
+ hdr = fin.read(sz)
+
+ if len(hdr) == 0:
+ return
+
+ if len(hdr) < sz:
+ raise StreamError("Junk found on the end of the stream")
+
+ (sig, length) = struct.unpack(LIBXL_QEMU_RECORD_HDR, hdr)
+
+ if sig != LIBXL_QEMU_SIGNATURE or length == 0:
+ raise StreamError("Junk found on the end of the stream")
+
+ qemu_record = fin.read(length)
+
+ if len(qemu_record) != length:
+ raise StreamError("Truncated qemu save record")
+
+ print("Libxl qemu save record, length %u" % (length, ))
+
+def main(argv = sys.argv):
+
+ if len(argv) == 2:
+ fin = open(argv[1], "rb")
+ else:
+ fin = sys.stdin
+
+ try:
+ verify_ihdr(fin)
+ verify_dhdr(fin)
+
+ while verify_record(fin) != REC_TYPE_end:
+ pass
+
+ verify_qemu_record(fin)
+
+ if fin.read(1) != "":
+ raise StreamError("Junk found on the end of the stream")
+
+ except (IOError, StreamError, RecordError) as e:
+ print "Error: ", e
+ return 1
+
+ except RuntimeError as e:
+ print "Script error", e
+ print "Please fix me"
+ return 2
+
+ print "Done"
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
--
1.7.10.4
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |