aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir.fraser@citrix.com>2009-08-14 17:26:23 +0100
committerKeir Fraser <keir.fraser@citrix.com>2009-08-14 17:26:23 +0100
commitb5eb2dabf5e264c612885fb45725122701a3b272 (patch)
tree23134bbe424b860cd05a6bd19d7c4653c1c9c114
parent2ca1b6fff7d18cf2049432ea78387cd326e8bbf2 (diff)
downloadxen-b5eb2dabf5e264c612885fb45725122701a3b272.tar.gz
xen-b5eb2dabf5e264c612885fb45725122701a3b272.tar.bz2
xen-b5eb2dabf5e264c612885fb45725122701a3b272.zip
xen-hvmctx: a tool to print the HVM state of a running domain
Signed-off-by: Tim Deegan <Tim.Deegan@citrix.com> Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
-rw-r--r--.hgignore1
-rw-r--r--tools/misc/Makefile6
-rw-r--r--tools/misc/xen-hvmctx.c450
3 files changed, 454 insertions, 3 deletions
diff --git a/.hgignore b/.hgignore
index d62e492c7b..227b6814a9 100644
--- a/.hgignore
+++ b/.hgignore
@@ -197,6 +197,7 @@
^tools/misc/xen-tmem-list-parse$
^tools/misc/xenperf$
^tools/misc/xenpm$
+^tools/misc/xen-hvmctx$
^tools/misc/gtraceview$
^tools/misc/gtracestat$
^tools/pygrub/build/.*$
diff --git a/tools/misc/Makefile b/tools/misc/Makefile
index 8c25f886ff..424a34a0a4 100644
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -10,7 +10,7 @@ CFLAGS += $(INCLUDES)
HDRS = $(wildcard *.h)
-TARGETS-y := xenperf xenpm xen-tmem-list-parse gtraceview gtracestat
+TARGETS-y := xenperf xenpm xen-tmem-list-parse gtraceview gtracestat xen-hvmctx
TARGETS-$(CONFIG_X86) += xen-detect
TARGETS := $(TARGETS-y)
@@ -22,7 +22,7 @@ INSTALL_BIN-y := xencons
INSTALL_BIN-$(CONFIG_X86) += xen-detect
INSTALL_BIN := $(INSTALL_BIN-y)
-INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm xen-tmem-list-parse gtraceview gtracestat
+INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm xen-tmem-list-parse gtraceview gtracestat xen-hvmctx
INSTALL_SBIN := $(INSTALL_SBIN-y)
DEFAULT_PYTHON_PATH := $(shell $(XEN_ROOT)/tools/python/get-path)
@@ -53,7 +53,7 @@ clean:
%.o: %.c $(HDRS) Makefile
$(CC) -c $(CFLAGS) -o $@ $<
-xenperf xenpm gtracestat: %: %.o Makefile
+xen-hvmctx xenperf xenpm gtracestat: %: %.o Makefile
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LDFLAGS_libxenctrl)
gtraceview: %: %.o Makefile
diff --git a/tools/misc/xen-hvmctx.c b/tools/misc/xen-hvmctx.c
new file mode 100644
index 0000000000..142ef2a41d
--- /dev/null
+++ b/tools/misc/xen-hvmctx.c
@@ -0,0 +1,450 @@
+/*
+ * xen-hvmctx.c
+ *
+ * Print out the contents of a HVM save record in a human-readable way.
+ *
+ * Tim Deegan <Tim.Deegan@citrix.com>
+ * Copyright (c) 2008 Citrix Systems (R&D) Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_TO_LONGS(bits) \
+ (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG)
+#define DECLARE_BITMAP(name,bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+#include <xenctrl.h>
+#include <xen/xen.h>
+#include <xen/domctl.h>
+#include <xen/hvm/save.h>
+
+static uint8_t *buf = NULL;
+static uint32_t len;
+static uint32_t off;
+
+#define READ(_x) do { \
+ if ( len - off < sizeof (_x) ) \
+ { \
+ fprintf(stderr, "Error: need another %u bytes, only %u available", \
+ (unsigned int)sizeof(_x), len - off); \
+ exit(1); \
+ } \
+ memcpy(&(_x), buf + off, sizeof (_x)); \
+ off += sizeof (_x); \
+} while (0)
+
+static void dump_header(void)
+{
+ HVM_SAVE_TYPE(HEADER) h;
+ READ(h);
+ printf(" Header: magic %#lx, version %lu\n",
+ (unsigned long) h.magic, (unsigned long) h.version);
+ printf(" Xen changeset %llx\n",
+ (unsigned long long) h.changeset);
+ printf(" CPUID[0][%%eax] 0x%.8lx\n", (unsigned long) h.cpuid);
+}
+
+struct fpu_mm {
+ uint64_t lo;
+ uint16_t hi;
+ uint16_t pad[3];
+} __attribute__((packed));
+
+struct fpu_xmm {
+ uint64_t lo;
+ uint64_t hi;
+};
+
+struct fpu_regs {
+ uint16_t fcw;
+ uint16_t fsw;
+ uint8_t ftw;
+ uint8_t res0;
+ uint16_t fop;
+ uint64_t fpuip;
+ uint64_t fpudp;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ struct fpu_mm mm[8];
+ struct fpu_xmm xmm[15];
+ uint64_t res1[12];
+} __attribute__((packed));
+
+static void dump_fpu(void *p)
+{
+ struct fpu_regs *r = p;
+ int i;
+
+ printf(" FPU: fcw 0x%4.4x fsw 0x%4.4x\n"
+ " ftw 0x%2.2x (0x%2.2x) fop 0x%4.4x\n"
+ " fpuip 0x%16.16"PRIx64" fpudp 0x%16.16"PRIx64"\n"
+ " mxcsr 0x%8.8lx mask 0x%8.8lx\n",
+ (unsigned)r->fcw, (unsigned)r->fsw,
+ (unsigned)r->ftw, (unsigned)r->res0, (unsigned)r->fop,
+ r->fpuip, r->fpudp,
+ (unsigned long)r->mxcsr, (unsigned long)r->mxcsr_mask);
+
+ for ( i = 0 ; i < 8 ; i++ )
+ printf(" mm%i 0x%4.4x%16.16"PRIx64" (0x%4.4x%4.4x%4.4x)\n",
+ i, r->mm[i].hi, r->mm[i].lo,
+ r->mm[i].pad[2], r->mm[i].pad[1], r->mm[i].pad[0]);
+
+ for ( i = 0 ; i < 16 ; i++ )
+ printf(" xmm%2.2i 0x%16.16"PRIx64"%16.16"PRIx64"\n",
+ i, r->xmm[i].hi, r->xmm[i].lo);
+
+ for ( i = 0 ; i < 6 ; i++ )
+ printf(" (0x%16.16"PRIx64"%16.16"PRIx64")\n",
+ r->res1[2*i+1], r->res1[2*i]);
+}
+
+static void dump_cpu(void)
+{
+ HVM_SAVE_TYPE(CPU) c;
+ READ(c);
+ printf(" CPU: rax 0x%16.16llx rbx 0x%16.16llx\n"
+ " rcx 0x%16.16llx rdx 0x%16.16llx\n"
+ " rbp 0x%16.16llx rsi 0x%16.16llx\n"
+ " rdi 0x%16.16llx rsp 0x%16.16llx\n"
+ " r8 0x%16.16llx r9 0x%16.16llx\n"
+ " r10 0x%16.16llx r11 0x%16.16llx\n"
+ " r12 0x%16.16llx r13 0x%16.16llx\n"
+ " r14 0x%16.16llx r15 0x%16.16llx\n"
+ " rip 0x%16.16llx rflags 0x%16.16llx\n"
+ " cr0 0x%16.16llx cr2 0x%16.16llx\n"
+ " cr3 0x%16.16llx cr4 0x%16.16llx\n"
+ " dr0 0x%16.16llx dr1 0x%16.16llx\n"
+ " dr2 0x%16.16llx dr3 0x%16.16llx\n"
+ " dr6 0x%16.16llx dr7 0x%16.16llx\n"
+ " cs 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " ds 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " es 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " fs 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " gs 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " ss 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " tr 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " ldtr 0x%8.8x (0x%16.16llx + 0x%8.8x / 0x%5.5x)\n"
+ " itdr (0x%16.16llx + 0x%8.8x)\n"
+ " gdtr (0x%16.16llx + 0x%8.8x)\n"
+ " sysenter cs 0x%8.8llx eip 0x%16.16llx esp 0x%16.16llx\n"
+ " shadow gs 0x%16.16llx\n"
+ " MSR flags 0x%16.16llx lstar 0x%16.16llx\n"
+ " star 0x%16.16llx cstar 0x%16.16llx\n"
+ " sfmask 0x%16.16llx efer 0x%16.16llx\n"
+ " tsc 0x%16.16llx\n"
+ " event 0x%8.8lx error 0x%8.8lx\n",
+ (unsigned long long) c.rax, (unsigned long long) c.rbx,
+ (unsigned long long) c.rcx, (unsigned long long) c.rdx,
+ (unsigned long long) c.rbp, (unsigned long long) c.rsi,
+ (unsigned long long) c.rdi, (unsigned long long) c.rsp,
+ (unsigned long long) c.r8, (unsigned long long) c.r9,
+ (unsigned long long) c.r10, (unsigned long long) c.r11,
+ (unsigned long long) c.r12, (unsigned long long) c.r13,
+ (unsigned long long) c.r14, (unsigned long long) c.r15,
+ (unsigned long long) c.rip, (unsigned long long) c.rflags,
+ (unsigned long long) c.cr0, (unsigned long long) c.cr2,
+ (unsigned long long) c.cr3, (unsigned long long) c.cr4,
+ (unsigned long long) c.dr0, (unsigned long long) c.dr1,
+ (unsigned long long) c.dr2, (unsigned long long) c.dr3,
+ (unsigned long long) c.dr6, (unsigned long long) c.dr7,
+ c.cs_sel, (unsigned long long) c.cs_base, c.cs_limit, c.cs_arbytes,
+ c.ds_sel, (unsigned long long) c.ds_base, c.ds_limit, c.ds_arbytes,
+ c.es_sel, (unsigned long long) c.es_base, c.es_limit, c.es_arbytes,
+ c.fs_sel, (unsigned long long) c.fs_base, c.fs_limit, c.fs_arbytes,
+ c.gs_sel, (unsigned long long) c.gs_base, c.gs_limit, c.gs_arbytes,
+ c.ss_sel, (unsigned long long) c.ss_base, c.ss_limit, c.ss_arbytes,
+ c.tr_sel, (unsigned long long) c.tr_base, c.tr_limit, c.tr_arbytes,
+ c.ldtr_sel, (unsigned long long) c.ldtr_base,
+ c.ldtr_limit, c.ldtr_arbytes,
+ (unsigned long long) c.idtr_base, c.idtr_limit,
+ (unsigned long long) c.gdtr_base, c.gdtr_limit,
+ (unsigned long long) c.sysenter_cs,
+ (unsigned long long) c.sysenter_eip,
+ (unsigned long long) c.sysenter_esp,
+ (unsigned long long) c.shadow_gs,
+ (unsigned long long) c.msr_flags,
+ (unsigned long long) c.msr_lstar,
+ (unsigned long long) c.msr_star,
+ (unsigned long long) c.msr_cstar,
+ (unsigned long long) c.msr_syscall_mask,
+ (unsigned long long) c.msr_efer,
+ (unsigned long long) c.tsc,
+ (unsigned long) c.pending_event, (unsigned long) c.error_code);
+ dump_fpu(&c.fpu_regs);
+}
+
+
+static void dump_pic(void)
+{
+ HVM_SAVE_TYPE(PIC) p;
+ READ(p);
+ printf(" PIC: IRQ base %#x, irr %#x, imr %#x, isr %#x\n",
+ p.irq_base, p.irr, p.imr, p.isr);
+
+ printf(" init_state %u, priority_add %u, readsel_isr %u, poll %u\n",
+ p.init_state, p.priority_add, p.readsel_isr, p.poll);
+ printf(" auto_eoi %u, rotate_on_auto_eoi %u\n",
+ p.auto_eoi, p.rotate_on_auto_eoi);
+ printf(" special_fully_nested_mode %u, special_mask_mode %u\n",
+ p.special_fully_nested_mode, p.special_mask_mode);
+ printf(" is_master %u, elcr %#x, int_output %#x\n",
+ p.is_master, p.elcr, p.int_output);
+}
+
+
+static void dump_ioapic(void)
+{
+ int i;
+ HVM_SAVE_TYPE(IOAPIC) p;
+ READ(p);
+ printf(" IOAPIC: base_address %#llx, ioregsel %#x id %#x\n",
+ (unsigned long long) p.base_address, p.ioregsel, p.id);
+ for ( i = 0; i < VIOAPIC_NUM_PINS; i++ )
+ {
+ printf(" pin %.2i: 0x%.16llx\n", i,
+ (unsigned long long) p.redirtbl[i].bits);
+ }
+}
+
+static void dump_lapic(void)
+{
+ HVM_SAVE_TYPE(LAPIC) p;
+ READ(p);
+ printf(" LAPIC: base_msr %#llx, disabled %#x, timer_divisor %#x\n",
+ (unsigned long long) p.apic_base_msr, p.disabled, p.timer_divisor);
+}
+
+static void dump_lapic_regs(void)
+{
+ unsigned int i;
+ HVM_SAVE_TYPE(LAPIC_REGS) r;
+ READ(r);
+ printf(" LAPIC registers:\n");
+ for ( i = 0 ; i < 0x400 ; i += 32 )
+ {
+ printf(" 0x%4.4x: 0x%16.16llx 0x%4.4x: 0x%16.16llx\n",
+ i, *(unsigned long long *)&r.data[i],
+ i + 16, *(unsigned long long *)&r.data[i + 16]);
+ }
+}
+
+static void dump_pci_irq(void)
+{
+ HVM_SAVE_TYPE(PCI_IRQ) i;
+ READ(i);
+ printf(" PCI IRQs: 0x%16.16llx%16.16llx\n",
+ (unsigned long long) i.pad[0], (unsigned long long) i.pad[1]);
+}
+
+static void dump_isa_irq(void)
+{
+ HVM_SAVE_TYPE(ISA_IRQ) i;
+ READ(i);
+ printf(" ISA IRQs: 0x%4.4llx\n",
+ (unsigned long long) i.pad[0]);
+}
+
+static void dump_pci_link(void)
+{
+ HVM_SAVE_TYPE(PCI_LINK) l;
+ READ(l);
+ printf(" PCI LINK: %u %u %u %u\n",
+ l.route[0], l.route[1], l.route[2], l.route[3]);
+}
+
+static void dump_pit(void)
+{
+ int i;
+ HVM_SAVE_TYPE(PIT) p;
+ READ(p);
+ printf(" PIT: speaker %s\n", p.speaker_data_on ? "on" : "off");
+ for ( i = 0 ; i < 2 ; i++ )
+ {
+ printf(" ch %1i: count %#x, latched_count %#x, count_latched %u\n",
+ i, p.channels[i].count, p.channels[i].latched_count,
+ p.channels[i].count_latched);
+ printf(" status %#x, status_latched %#x\n",
+ p.channels[i].status, p.channels[i].status_latched);
+ printf(" rd_state %#x, wr_state %#x, wr_latch %#x, rw_mode %#x\n",
+ p.channels[i].read_state, p.channels[i].write_state,
+ p.channels[i].write_latch, p.channels[i].rw_mode);
+ printf(" mode %#x, bcd %#x, gate %#x\n",
+ p.channels[i].mode, p.channels[i].bcd, p.channels[i].gate);
+ }
+}
+
+static void dump_rtc(void)
+{
+ HVM_SAVE_TYPE(RTC) r;
+ READ(r);
+ printf(" RTC: regs 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ r.cmos_data[0], r.cmos_data[1], r.cmos_data[2], r.cmos_data[3],
+ r.cmos_data[4], r.cmos_data[5], r.cmos_data[6], r.cmos_data[7]);
+ printf(" 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x, index 0x%2.2x\n",
+ r.cmos_data[8], r.cmos_data[9], r.cmos_data[10], r.cmos_data[11],
+ r.cmos_data[12], r.cmos_data[13], r.cmos_index);
+
+}
+
+static void dump_hpet(void)
+{
+ int i;
+ HVM_SAVE_TYPE(HPET) h;
+ READ(h);
+ printf(" HPET: capability %#llx config %#llx\n",
+ (unsigned long long) h.capability,
+ (unsigned long long) h.config);
+ printf(" isr %#llx counter %#llx\n",
+ (unsigned long long) h.isr,
+ (unsigned long long) h.mc64);
+ for ( i = 0; i < HPET_TIMER_NUM; i++ )
+ {
+ printf(" timer%i config %#llx cmp %#llx\n", i,
+ (unsigned long long) h.timers[i].config,
+ (unsigned long long) h.timers[i].cmp);
+ printf(" timer%i period %#llx fsb %#llx\n", i,
+ (unsigned long long) h.period[i],
+ (unsigned long long) h.timers[i].fsb);
+ }
+}
+
+static void dump_pmtimer(void)
+{
+ HVM_SAVE_TYPE(PMTIMER) p;
+ READ(p);
+ printf(" ACPI PM: TMR_VAL 0x%x, PM1a_STS 0x%x, PM1a_EN 0x%x\n",
+ p.tmr_val, (unsigned) p.pm1a_sts, (unsigned) p.pm1a_en);
+}
+
+static void dump_mtrr(void)
+{
+ HVM_SAVE_TYPE(MTRR) p;
+ int i;
+ READ(p);
+ printf(" MTRR: PAT 0x%llx, cap 0x%llx, default 0x%llx\n",
+ (unsigned long long) p.msr_pat_cr,
+ (unsigned long long) p.msr_mtrr_cap,
+ (unsigned long long) p.msr_mtrr_def_type);
+ for ( i = 0 ; i < MTRR_VCNT ; i++ )
+ printf(" var %i 0x%16.16llx 0x%16.16llx\n", i,
+ (unsigned long long) p.msr_mtrr_var[2 * i],
+ (unsigned long long) p.msr_mtrr_var[2 * i + 1]);
+ for ( i = 0 ; i < NUM_FIXED_MSR ; i++ )
+ printf(" fixed %.2i 0x%16.16llx\n", i,
+ (unsigned long long) p.msr_mtrr_fixed[i]);
+}
+
+static void dump_viridian(void)
+{
+ HVM_SAVE_TYPE(VIRIDIAN) p;
+ READ(p);
+ printf(" VIRIDIAN: hypercall gpa 0x%llx, guest ID 0x%llx\n",
+ (unsigned long long) p.hypercall_gpa,
+ (unsigned long long) p.guest_os_id);
+}
+
+int main(int argc, char **argv)
+{
+ int entry, domid, xch;
+
+ struct hvm_save_descriptor desc;
+
+ if ( argc != 2 || !argv[1] || (domid = atoi(argv[1])) < 0 )
+ {
+ fprintf(stderr, "usage: %s <domid>\n", argv[0]);
+ exit(1);
+ }
+
+ xch = xc_interface_open();
+ if ( xch < 0 )
+ {
+ fprintf(stderr, "Error: can't open libxc handle\n");
+ exit(1);
+ }
+ len = xc_domain_hvm_getcontext(xch, domid, 0, 0);
+ if ( len == (uint32_t) -1 )
+ {
+ fprintf(stderr, "Error: can't get record length for dom %i\n", domid);
+ exit(1);
+ }
+ buf = malloc(len);
+ if ( buf == NULL )
+ {
+ fprintf(stderr, "Error: can't allocate %u bytes\n", len);
+ exit(1);
+ }
+ len = xc_domain_hvm_getcontext(xch, domid, buf, len);
+ if ( len == (uint32_t) -1 )
+ {
+ fprintf(stderr, "Error: can't get HVM record for dom %i\n", domid);
+ exit(1);
+ }
+ off = 0;
+
+ /* Say hello */
+ printf("HVM save record for domain %i\n", domid);
+
+ entry = 0;
+ do {
+ READ(desc);
+ printf("Entry %i: type %u instance %u, length %u\n",
+ entry++, (unsigned) desc.typecode,
+ (unsigned) desc.instance, (unsigned) desc.length);
+ switch (desc.typecode)
+ {
+ case HVM_SAVE_CODE(HEADER): dump_header(); break;
+ case HVM_SAVE_CODE(CPU): dump_cpu(); break;
+ case HVM_SAVE_CODE(PIC): dump_pic(); break;
+ case HVM_SAVE_CODE(IOAPIC): dump_ioapic(); break;
+ case HVM_SAVE_CODE(LAPIC): dump_lapic(); break;
+ case HVM_SAVE_CODE(LAPIC_REGS): dump_lapic_regs(); break;
+ case HVM_SAVE_CODE(PCI_IRQ): dump_pci_irq(); break;
+ case HVM_SAVE_CODE(ISA_IRQ): dump_isa_irq(); break;
+ case HVM_SAVE_CODE(PCI_LINK): dump_pci_link(); break;
+ case HVM_SAVE_CODE(PIT): dump_pit(); break;
+ case HVM_SAVE_CODE(RTC): dump_rtc(); break;
+ case HVM_SAVE_CODE(HPET): dump_hpet(); break;
+ case HVM_SAVE_CODE(PMTIMER): dump_pmtimer(); break;
+ case HVM_SAVE_CODE(MTRR): dump_mtrr(); break;
+ case HVM_SAVE_CODE(VIRIDIAN): dump_viridian(); break;
+ case HVM_SAVE_CODE(END): break;
+ default:
+ printf(" ** Don't understand type %u: skipping\n",
+ (unsigned) desc.typecode);
+ off += (desc.length);
+ }
+ } while ( desc.typecode != HVM_SAVE_CODE(END) && off < len );
+
+ return 0;
+}