[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 3/6] tools/libxc: Scripts for inspection/valdiation of legacy and new streams
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx> Signed-off-by: Frediano Ziglio <frediano.ziglio@xxxxxxxxxx> --- tools/libxc/saverestore/scripts/generate.py | 59 ++++ .../libxc/saverestore/scripts/inspect-legacy32.py | 167 ++++++++++ tools/libxc/saverestore/scripts/streamspec.py | 89 ++++++ tools/libxc/saverestore/scripts/verify.py | 330 ++++++++++++++++++++ 4 files changed, 645 insertions(+) create mode 100755 tools/libxc/saverestore/scripts/generate.py create mode 100755 tools/libxc/saverestore/scripts/inspect-legacy32.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/generate.py b/tools/libxc/saverestore/scripts/generate.py new file mode 100755 index 0000000..3b01e65 --- /dev/null +++ b/tools/libxc/saverestore/scripts/generate.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from streamspec import * +import struct, sys + +ihdr = struct.pack(IHDR_FORMAT, + 0xffffffffffffffff, # Marker + IHDR_IDENT, # "XENF" in ASCII + 1, # Version + IHDR_OPT_LE, # Options + 0, 0 # Reserved + ) + +def emit_record(type, data): + length = len(data) + + r = struct.pack(RH_FORMAT, type, length) + r += data + + padding_len = (8 - (length & 7)) & 7 + r += '\x00' * padding_len + + sys.stdout.write(r) + + +def emit_pv(): + + dhdr = struct.pack(DHDR_FORMAT, + DHDR_TYPE_x86_pv, # Type + 12, # Page size + 0, # Reserved + 4, # Xen major + 5 # Xen minor + ) + + sys.stdout.write(ihdr) + sys.stdout.write(dhdr) + + x86_pv_info = struct.pack(X86_PV_INFO_FORMAT, + 8, # Guest width + 4, # Guest levels + 0 # Options + ) + + emit_record(REC_TYPE_x86_pv_info, x86_pv_info) + emit_record(REC_TYPE_end, "") + return 0 + + +def main(argv = sys.argv): + + if len(argv) == 0 or argv[0] == "pv": + return emit_pv() + else: + return 1 + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/tools/libxc/saverestore/scripts/inspect-legacy32.py b/tools/libxc/saverestore/scripts/inspect-legacy32.py new file mode 100755 index 0000000..e20a8f2 --- /dev/null +++ b/tools/libxc/saverestore/scripts/inspect-legacy32.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import struct + +fin = None +guest_width = 0 +guest_levels = 0 + +class StreamError(StandardError): + pass + +def rdexact(n): + _ = fin.read(n) + if len(_) != n: + raise IOError("Stream truncated") + #print"> read 0x%x bytes" % (n, ) + return _ + +def unpack_exact(fmt): + l = struct.calcsize(fmt) + return struct.unpack(fmt, rdexact(l)) + +def read_extended_info(): + global guest_width, guest_level + + sig, rem_length = struct.unpack("II", rdexact(8)) + + if sig != 0xffffffff: + raise StreamError("Bad extended info signature 0x%08x" % (sig,)) + else: + print "Endended Info: length 0x%x" % (rem_length,) + + so_far = 0 + while so_far < rem_length: + + blkid, datasz = struct.unpack("4sI", rdexact(8)) + so_far += 8 + + print " Record type: %s, size 0x%x" % (blkid, datasz) + + # Eww, but this is how it is done :( + if blkid == "vcpu": + if datasz == 0x1430: + guest_width = 8 + guest_levels = 4 + print " 64bit domain, 4 levels" + else: + raise StreamError("Unable to determine guest width/level") + + rdexact(datasz) + so_far += datasz + + if so_far != rem_length: + raise StreamError("Overshot total Extended Info size. Consumed 0x%x bytes" % (so_far,)) + +def read_chunks(): + + while True: + + chunk_type, = struct.unpack("i", rdexact(4)) + print "Chunk: type 0x%x" % (chunk_type,) + + if chunk_type == 0: + print " End" + return + + elif chunk_type > 0: + print " Page Batch" + pfn_array = rdexact(chunk_type * 4) + page_data = rdexact(chunk_type * 4096) + + elif chunk_type == -2: + max_id, = unpack_exact("i") + bitmap = rdexact(((max_id/64) + 1) * 8) + print " Vcpu info: max_id %d" % (max_id, ) + + elif chunk_type == -7: + mode, nsec, khz, incarn = unpack_exact("IQII") + print " TSC_INFO: mode %s, %d ns, %d khz, %d incarn" % ( mode, nsec, khz, incarn) + + elif chunk_type == -9: + print " Last Checkpoint" + + elif chunk_type == -12: + sz, = unpack_exact("I") + data = rdexact(sz) + print " Compressed Data: sz 0x%x" % (sz, ) + + elif chunk_type == -18: + sz, = unpack_exact("I") + data = rdexact(sz) + print " Toolstack Data: sz 0x%x" % (sz, ) + + else: + raise StreamError("Unrecognised chunk") + +def main(argv = sys.argv): + global fin + + if len(argv) == 2: + fin = open(argv[1], "rb") + else: + fin = sys.stdin + + try: + # Skip Xl header + if "Xen saved domain, xl format\n \0 \r" != rdexact(32): + raise StreamError("No xl header") + + _, _, _, optlen = struct.unpack("=IIII", rdexact(16)) + rdexact(optlen) + print "xl header skipped" + + # P2M size + p2m_size, = struct.unpack("I", rdexact(4)) + print "P2M Size: 0x%x" % (p2m_size,) + + # Extended info + read_extended_info() + + # P2M list + + fpp = 4096/guest_width + p2m_len = (p2m_size + fpp - 1) / fpp + + print "Reading p2m frames. fpp: %d, p2m_len: %d" % (fpp, p2m_len) + + p2m_frames = rdexact(p2m_len * 4) + if p2m_len < 20: + print list(struct.unpack("I" * p2m_len, p2m_frames)) + + read_chunks() + + unmapped_pfn_count, = unpack_exact("I") + unmapped_pfn_list = rdexact(unmapped_pfn_count * 4) + print "Unmapped PFN count: 0x%x" % (unmapped_pfn_count, ) + + # VCPU Context fudge + _ = rdexact(0x1430) + _ = rdexact(128) + xfeature_mask, xsize = unpack_exact("QQ") + _ = rdexact(xsize) + print "Got VCPU information" + + shared_info = rdexact(4096) + print "Got shinfo" + + if fin.read(1) != "": + raise StreamError("Junk found on the end of the stream") + + except (IOError, StreamError, ) 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)) diff --git a/tools/libxc/saverestore/scripts/streamspec.py b/tools/libxc/saverestore/scripts/streamspec.py new file mode 100644 index 0000000..12b351e --- /dev/null +++ b/tools/libxc/saverestore/scripts/streamspec.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Image Header +IHDR_FORMAT = "!QIIHHI" + +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_x86_pv_shared_info = 0x00000007 +REC_TYPE_tsc_info = 0x00000008 + +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_x86_pv_shared_info : "x86 PV shared info", + REC_TYPE_tsc_info : "TSC info", +} + +# 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 = "=BBB" + +X86_PV_INFO_OPT_VMASST_ = 0 +X86_PV_INFO_OPT_VMASST = (1 << X86_PV_INFO_OPT_VMASST_) + +X86_PV_INFO_OPT_RESZ_MASK = 0xfe + +# x86_pv_vcpu_{basic,extended} +X86_PV_VCPU_FORMAT = "=II" + +# x86_pv_vcpu_xsave +X86_PV_VCPU_XSAVE_FORMAT = "=IIQ" + +# tsc_info +TSC_INFO_FORMAT = "=IIQI" diff --git a/tools/libxc/saverestore/scripts/verify.py b/tools/libxc/saverestore/scripts/verify.py new file mode 100755 index 0000000..0b1ec14 --- /dev/null +++ b/tools/libxc/saverestore/scripts/verify.py @@ -0,0 +1,330 @@ +#!/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("Wrong size") + + +def verify_record_x86_pv_vcpu_generic(content, name): + # Generic for both REC_TYPE_x86_pv_vcpu_{basic,extended} + minsz = struct.calcsize(X86_PV_VCPU_FORMAT) + + 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, 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_record_x86_pv_vcpu_xsave(content): + minsz = struct.calcsize(X86_PV_VCPU_XSAVE_FORMAT) + + if len(content) <= minsz: + raise RecordError("X86_PV_VCPU_XSAVE record length must be at least %d" + " bytes long" % (minsz, )) + + vcpuid, res1, xmask = struct.unpack_from(X86_PV_VCPU_XSAVE_FORMAT, + content) + + if res1 != 0: + raise StreamError("Reserved bits set in X86_PV_VCPU_XSAVE record " + "0x%04x" % (res1, )) + + print " vcpu%d xsave context, mask 0x%x" % (vcpuid, xmask) + + +def verify_x86_pv_info(content): + + if len(content) != 3: + raise RecordError("x86_pf_info: expected length of 3, got %d" + % (len(content), )) + + width, levels, options = 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 (4, 8): + raise RecordError("Expected levels of 3 or 4, got %d" % (levels, )) + + if (options & X86_PV_INFO_OPT_RESZ_MASK) != 0: + raise StreamError("Reserved bits set in X86_PV_INFO options: 0x%02x" + % (options & X86_PV_INFO_OPT_RESZ_MASK, )) + + 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_x86_pv_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 = struct.unpack(TSC_INFO_FORMAT, content) + print (" Mode %u, %u kHz, %u ns, incarnation %d" + % (mode, khz, nsec, incarn)) + +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 : verify_record_x86_pv_vcpu_xsave, + + REC_TYPE_x86_pv_shared_info: verify_record_x86_pv_shared_info, + REC_TYPE_tsc_info: verify_record_tsc_info, +} + +_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 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 + + 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 |